<commit_before>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use nom::*;
use nom::IResult::*;
use nom::Err::*;

use twig::{Twig, Wing};

/// Match at least two spaces or one newline.
pub fn gap(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for item in input.iter() {
        if *item == '\t' as u8 {
            // Physical tabs are banned.
            // XXX: Should have own ErrorKind tag.
            return Error(Position(ErrorKind::MultiSpace, input));
        }

        if *item == '\n' as u8 {
            spaces += 2;
        } else if is_space(*item) {
            spaces += 1;
        }

        if !is_space(*item) && *item != '\r' as u8 && *item != '\n' as u8 {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Error(Position(ErrorKind::MultiSpace, input))
    } else {
        Done(&input[idx..], &input[0..idx])
    }
}

pub fn ident(input:&[u8]) -> IResult<&[u8], &[u8]> {
    for (idx, item) in input.iter().enumerate() {
        if idx == 0 {
            if !is_alphabetic(*item) {
                return Error(Position(ErrorKind::Alpha, input))
            }
        } else {
            if !is_alphabetic(*item) && !is_digit(*item) && *item != '-' as u8 {
                return Done(&input[idx..], &input[0..idx])
            }
        }
    }
    Error(Position(ErrorKind::Alpha, input))
}

/// Parse a Hoon expression into an AST.
named!(pub ream<Twig>,
    alt!(
        atom
      | Brhp
      | Dtls
      | Dtts
      | Ktts
      | Tsgr
      | Tsls
      | Wtcl

        // TODO: Rest of hoon
    )
);

// TODO: Handle separator dots
// TODO: Handle other odors than ud.
named!(atom<Twig>,
  map_res!(
    map_res!(
      map_res!(
        digit,
        str::from_utf8
      ),
      FromStr::from_str
    ),
    |x| Ok::<Twig, ()>(Twig::Dtzy("ud".to_string(), x))
  )
);

named!(id<&str>,
    map_res!(
        ident,
        str::from_utf8
    )
);

named!(p<(Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream),
            || { Box::new(p) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream,
                || { Box::new(p) }),
            tag!(")"))
    )
);

named!(pq<(Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream,
                || { (Box::new(p), Box::new(q)) }),
            tag!(")"))
    )
);

named!(pqr<(Box<Twig>, Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream) ~
            r: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q), Box::new(r)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream ~
                space ~
                r: ream,
                || { (Box::new(p), Box::new(q), Box::new(r)) }),
            tag!(")"))
    )
);

/// Regular rune with 1 argument
macro_rules! rune1 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               p: p,
               || {
                 Twig::$id(p)
               }
            )
        );
    }
}

/// Regular rune with 2 arguments
macro_rules! rune2 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pq,
               || {
                 let (p, q) = a.clone();
                 Twig::$id(p, q)
               }
            )
        );
    }
}

/// Regular rune with 3 arguments
macro_rules! rune3 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pqr,
               || {
                 let (p, q, r) = a.clone();
                 Twig::$id(p, q, r)
               }
            )
        );
    }
}

named!(wing<Wing>,
    chain!(
        x: id ~
        mut xs: many0!(
            chain!(
                tag!(".") ~
                x: id,
                || { x.to_string() }
            )
        ),
        || {
            xs.insert(0, x.to_string());
            xs
        }
    )
);

rune1!(Brhp, "|-");
rune1!(Dtls, ".+");
rune2!(Dtts, ".=");
rune2!(Ktts, "^=");
rune2!(Tsgr, "=>");
rune2!(Tsls, "=+");
rune3!(Wtcl, "?:");

/*
named!(Cnts<Twig>,
    // TODO: Tall form
    delimited!(
        tag!("%=("),
        chain!(
            p: wing ~
            space ~
            q1: wing ~
            space ~
            q2: ream ~
            rs: many0!(
                chain!(
                    tag!(",") ~
                    space ~
                    r1: wing ~
                    space ~
                    r2: ream,
                || { (r1, r2) }
                )
            ),

            || {
                rs.insert(0, (q1, q2));
                Twig::Cnts(p, rs)
            }),
        tag!(")"))
);
*/

#[cfg(test)]
mod test {
    use std::rc::Rc;
    use num::FromPrimitive;
    use num::bigint::BigUint;
    use nom::IResult;
    use twig::Twig;
    use super::{gap, atom, ream};

    #[test]
    fn test_parse_atom() {
        assert_eq!(ream(&b"1234"[..]),
                   IResult::Done(&b""[..],
                                 Twig::Dtzy("ud".to_string(), BigUint::from_u32(1234).unwrap())));
    }

    #[test]
    fn test_parse_gap() {
        assert!(gap(&b"  "[..]).is_done());
        assert!(gap(&b"    "[..]).is_done());
        assert!(gap(&b"\n"[..]).is_done());
        assert!(gap(&b"\n  "[..]).is_done());
        assert!(gap(&b"  \n  "[..]).is_done());
        assert!(!gap(&b" "[..]).is_done());
    }
}
<commit_msg>Untested comment parser<commit_after>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use nom::*;
use nom::IResult::*;
use nom::Err::*;

use twig::{Twig, Wing};

/// Match at least two spaces or one newline.
pub fn gap(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for item in input.iter() {
        if *item == '\t' as u8 {
            // Physical tabs are banned.
            // XXX: Should have own ErrorKind tag.
            return Error(Position(ErrorKind::MultiSpace, input));
        }

        if *item == '\n' as u8 {
            spaces += 2;
        } else if is_space(*item) {
            spaces += 1;
        }

        if !is_space(*item) && *item != '\r' as u8 && *item != '\n' as u8 {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Error(Position(ErrorKind::MultiSpace, input))
    } else {
        Done(&input[idx..], &input[0..idx])
    }
}

pub fn ident(input:&[u8]) -> IResult<&[u8], &[u8]> {
    for (idx, item) in input.iter().enumerate() {
        if idx == 0 {
            if !is_alphabetic(*item) {
                return Error(Position(ErrorKind::Alpha, input))
            }
        } else {
            if !is_alphabetic(*item) && !is_digit(*item) && *item != '-' as u8 {
                return Done(&input[idx..], &input[0..idx])
            }
        }
    }
    Error(Position(ErrorKind::Alpha, input))
}

/// Parse a Hoon expression into an AST.
named!(pub ream<Twig>,
    alt!(
        atom
      | Brhp
      | Dtls
      | Dtts
      | Ktts
      | Tsgr
      | Tsls
      | Wtcl

        // TODO: Rest of hoon
    )
);

named!(comment<&[u8]>,
    chain!(
        tag!("::") ~
        x: take_until_and_consume!("\n"),
        || { x }
    )
);

// TODO: Handle separator dots
// TODO: Handle other odors than ud.
named!(atom<Twig>,
  map_res!(
    map_res!(
      map_res!(
        digit,
        str::from_utf8
      ),
      FromStr::from_str
    ),
    |x| Ok::<Twig, ()>(Twig::Dtzy("ud".to_string(), x))
  )
);

named!(id<&str>,
    map_res!(
        ident,
        str::from_utf8
    )
);

named!(p<(Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream),
            || { Box::new(p) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream,
                || { Box::new(p) }),
            tag!(")"))
    )
);

named!(pq<(Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream,
                || { (Box::new(p), Box::new(q)) }),
            tag!(")"))
    )
);

named!(pqr<(Box<Twig>, Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream) ~
            r: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q), Box::new(r)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream ~
                space ~
                r: ream,
                || { (Box::new(p), Box::new(q), Box::new(r)) }),
            tag!(")"))
    )
);

/// Regular rune with 1 argument
macro_rules! rune1 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               p: p,
               || {
                 Twig::$id(p)
               }
            )
        );
    }
}

/// Regular rune with 2 arguments
macro_rules! rune2 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pq,
               || {
                 let (p, q) = a.clone();
                 Twig::$id(p, q)
               }
            )
        );
    }
}

/// Regular rune with 3 arguments
macro_rules! rune3 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pqr,
               || {
                 let (p, q, r) = a.clone();
                 Twig::$id(p, q, r)
               }
            )
        );
    }
}

named!(wing<Wing>,
    chain!(
        x: id ~
        mut xs: many0!(
            chain!(
                tag!(".") ~
                x: id,
                || { x.to_string() }
            )
        ),
        || {
            xs.insert(0, x.to_string());
            xs
        }
    )
);

rune1!(Brhp, "|-");
rune1!(Dtls, ".+");
rune2!(Dtts, ".=");
rune2!(Ktts, "^=");
rune2!(Tsgr, "=>");
rune2!(Tsls, "=+");
rune3!(Wtcl, "?:");

/*
named!(Cnts<Twig>,
    // TODO: Tall form
    delimited!(
        tag!("%=("),
        chain!(
            p: wing ~
            space ~
            q1: wing ~
            space ~
            q2: ream ~
            rs: many0!(
                chain!(
                    tag!(",") ~
                    space ~
                    r1: wing ~
                    space ~
                    r2: ream,
                || { (r1, r2) }
                )
            ),

            || {
                rs.insert(0, (q1, q2));
                Twig::Cnts(p, rs)
            }),
        tag!(")"))
);
*/

#[cfg(test)]
mod test {
    use std::rc::Rc;
    use num::FromPrimitive;
    use num::bigint::BigUint;
    use nom::IResult;
    use twig::Twig;
    use super::{gap, atom, ream};

    #[test]
    fn test_parse_atom() {
        assert_eq!(ream(&b"1234"[..]),
                   IResult::Done(&b""[..],
                                 Twig::Dtzy("ud".to_string(), BigUint::from_u32(1234).unwrap())));
    }

    #[test]
    fn test_parse_gap() {
        assert!(gap(&b"  "[..]).is_done());
        assert!(gap(&b"    "[..]).is_done());
        assert!(gap(&b"\n"[..]).is_done());
        assert!(gap(&b"\n  "[..]).is_done());
        assert!(gap(&b"  \n  "[..]).is_done());
        assert!(!gap(&b" "[..]).is_done());
    }
}
<|endoftext|>


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

//! Rust wrappers around the raw JS apis

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::ffi;
use std::rc;
use std::str;
use std::u32;
use jsapi::*;
use jsapi::JSVersion::JSVERSION_LATEST;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSOPTION_DONT_REPORT_UNCAUGHT;
use JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING;
use ERR;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

/// A wrapper for the `JSRuntime` and `JSContext` structures in SpiderMonkey.
#[derive(Clone)]
pub struct Runtime {
    pub rt: rt,
    pub cx: rc::Rc<Cx>,
}

impl Runtime {
    /// Creates a new `JSRuntime` and `JSContext`.
    pub fn new() -> Runtime {
        let js_runtime = unsafe { JS_Init(default_heapsize) };
        assert!(!js_runtime.is_null());

        // Unconstrain the runtime's threshold on nominal heap size, to avoid
        // triggering GC too often if operating continuously near an arbitrary
        // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
        // still in effect to cause periodical, and we hope hygienic,
        // last-ditch GCs from within the GC's allocator.
        unsafe {
            JS_SetGCParameter(js_runtime, JSGC_MAX_BYTES, u32::MAX);
        }

        let js_context = unsafe {
            JS_NewContext(js_runtime, default_stacksize as size_t)
        };
        assert!(!js_context.is_null());

        unsafe {
            JS_SetOptions(js_context,
                          JSOPTION_VAROBJFIX |
                          JSOPTION_METHODJIT |
                          JSOPTION_TYPE_INFERENCE |
                          JSOPTION_DONT_REPORT_UNCAUGHT |
                          JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING);

            JS_SetVersion(js_context, JSVERSION_LATEST);
            JS_SetErrorReporter(js_context,
                                Some(reportError as unsafe extern "C"
                                     fn(*mut JSContext, *const c_char, *mut JSErrorReport)));
            JS_SetGCZeal(js_context, 0, JS_DEFAULT_ZEAL_FREQ);
        }

        let js_runtime = rc::Rc::new(rt_rsrc {
            ptr: js_runtime
        });
        let js_context = rc::Rc::new(Cx {
            ptr: js_context,
            rt: js_runtime.clone(),
        });
        Runtime {
            rt: js_runtime,
            cx: js_context,
        }
    }

    /// Returns the `JSRuntime` object.
    pub fn rt(&self) -> *mut JSRuntime {
        self.rt.ptr
    }

    /// Returns the `JSContext` object.
    pub fn cx(&self) -> *mut JSContext {
        self.cx.ptr
    }

    pub fn evaluate_script(&self, global: *mut JSObject, script: String,
                           filename: String, line_num: usize)
                           -> Result<(), ()> {
        let script_utf16: Vec<u16> = script.utf16_units().collect();
        let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
        debug!("Evaluating script from {} with content {}", filename, script);

        // SpiderMonkey does not approve of null pointers.
        let (ptr, len) = if script_utf16.len() == 0 {
            static empty: &'static [u16] = &[];
            (empty.as_ptr(), 0)
        } else {
            (script_utf16.as_ptr(), script_utf16.len() as c_uint)
        };
        assert!(!ptr.is_null());

        let mut rval: JSVal = NullValue();
        let result = unsafe {
            JS_EvaluateUCScript(self.cx(), global, ptr, len,
                                filename_cstr.as_ptr(), line_num as c_uint,
                                &mut rval)
        };

        if result == ERR {
            debug!("...err!");
            Err(())
        } else {
            // we could return the script result but then we'd have
            // to root it and so forth and, really, who cares?
            debug!("...ok!");
            Ok(())
        }
    }
}

pub type rt = rc::Rc<rt_rsrc>;

pub struct rt_rsrc {
    pub ptr : *mut JSRuntime,
}

impl Drop for rt_rsrc {
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    pub ptr: *mut JSContext,
    pub rt: rt,
}

impl Drop for Cx {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub unsafe extern fn reportError(_cx: *mut JSContext, msg: *const c_char, report: *mut JSErrorReport) {
    let fnptr = (*report).filename;
    let fname = if !fnptr.is_null() {
        let c_str = ffi::CStr::from_ptr(fnptr);
        str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string()
    } else {
        "none".to_string()
    };
    let lineno = (*report).lineno;
    let c_str = ffi::CStr::from_ptr(msg);
    let msg = str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string();
    error!("Error at {}:{}: {}\n", fname, lineno, msg);
}

pub fn with_compartment<R, F: FnMut() -> R>(cx: *mut JSContext, object: *mut JSObject, mut cb: F) -> R {
    unsafe {
        let call = JS_EnterCrossCompartmentCall(cx, object);
        let result = cb();
        JS_LeaveCrossCompartmentCall(call);
        result
    }
}

#[cfg(test)]
pub mod test {
    use super::Runtime;

    #[test]
    pub fn dummy() {
        let _rt = Runtime::new();
    }

}
<commit_msg>Remove reference counting from the Runtime implementation.<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/. */

//! Rust wrappers around the raw JS apis

use libc::types::os::arch::c95::{size_t, c_uint};
use libc::c_char;
use std::ffi;
use std::str;
use std::u32;
use jsapi::*;
use jsapi::JSVersion::JSVERSION_LATEST;
use jsval::{JSVal, NullValue};
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSOPTION_DONT_REPORT_UNCAUGHT;
use JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING;
use ERR;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

/// A wrapper for the `JSRuntime` and `JSContext` structures in SpiderMonkey.
pub struct Runtime {
    rt: *mut JSRuntime,
    cx: *mut JSContext,
}

impl Runtime {
    /// Creates a new `JSRuntime` and `JSContext`.
    pub fn new() -> Runtime {
        let js_runtime = unsafe { JS_Init(default_heapsize) };
        assert!(!js_runtime.is_null());

        // Unconstrain the runtime's threshold on nominal heap size, to avoid
        // triggering GC too often if operating continuously near an arbitrary
        // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
        // still in effect to cause periodical, and we hope hygienic,
        // last-ditch GCs from within the GC's allocator.
        unsafe {
            JS_SetGCParameter(js_runtime, JSGC_MAX_BYTES, u32::MAX);
        }

        let js_context = unsafe {
            JS_NewContext(js_runtime, default_stacksize as size_t)
        };
        assert!(!js_context.is_null());

        unsafe {
            JS_SetOptions(js_context,
                          JSOPTION_VAROBJFIX |
                          JSOPTION_METHODJIT |
                          JSOPTION_TYPE_INFERENCE |
                          JSOPTION_DONT_REPORT_UNCAUGHT |
                          JSOPTION_AUTOJSAPI_OWNS_ERROR_REPORTING);

            JS_SetVersion(js_context, JSVERSION_LATEST);
            JS_SetErrorReporter(js_context,
                                Some(reportError as unsafe extern "C"
                                     fn(*mut JSContext, *const c_char, *mut JSErrorReport)));
            JS_SetGCZeal(js_context, 0, JS_DEFAULT_ZEAL_FREQ);
        }

        Runtime {
            rt: js_runtime,
            cx: js_context,
        }
    }

    /// Returns the `JSRuntime` object.
    pub fn rt(&self) -> *mut JSRuntime {
        self.rt
    }

    /// Returns the `JSContext` object.
    pub fn cx(&self) -> *mut JSContext {
        self.cx
    }

    pub fn evaluate_script(&self, global: *mut JSObject, script: String,
                           filename: String, line_num: usize)
                           -> Result<(), ()> {
        let script_utf16: Vec<u16> = script.utf16_units().collect();
        let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
        debug!("Evaluating script from {} with content {}", filename, script);

        // SpiderMonkey does not approve of null pointers.
        let (ptr, len) = if script_utf16.len() == 0 {
            static empty: &'static [u16] = &[];
            (empty.as_ptr(), 0)
        } else {
            (script_utf16.as_ptr(), script_utf16.len() as c_uint)
        };
        assert!(!ptr.is_null());

        let mut rval: JSVal = NullValue();
        let result = unsafe {
            JS_EvaluateUCScript(self.cx(), global, ptr, len,
                                filename_cstr.as_ptr(), line_num as c_uint,
                                &mut rval)
        };

        if result == ERR {
            debug!("...err!");
            Err(())
        } else {
            // we could return the script result but then we'd have
            // to root it and so forth and, really, who cares?
            debug!("...ok!");
            Ok(())
        }
    }
}

impl Drop for Runtime {
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.cx);
            JS_Finish(self.rt);
        }
    }
}

pub unsafe extern fn reportError(_cx: *mut JSContext, msg: *const c_char, report: *mut JSErrorReport) {
    let fnptr = (*report).filename;
    let fname = if !fnptr.is_null() {
        let c_str = ffi::CStr::from_ptr(fnptr);
        str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string()
    } else {
        "none".to_string()
    };
    let lineno = (*report).lineno;
    let c_str = ffi::CStr::from_ptr(msg);
    let msg = str::from_utf8(c_str.to_bytes()).ok().unwrap().to_string();
    error!("Error at {}:{}: {}\n", fname, lineno, msg);
}

pub fn with_compartment<R, F: FnMut() -> R>(cx: *mut JSContext, object: *mut JSObject, mut cb: F) -> R {
    unsafe {
        let call = JS_EnterCrossCompartmentCall(cx, object);
        let result = cb();
        JS_LeaveCrossCompartmentCall(call);
        result
    }
}

#[cfg(test)]
pub mod test {
    use super::Runtime;

    #[test]
    pub fn dummy() {
        let _rt = Runtime::new();
    }

}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::UnknownUnit;
use length::Length;
use scale_factor::ScaleFactor;
use num::*;

use num_traits::NumCast;
use std::fmt;
use std::ops::{Mul, Div};
use std::marker::PhantomData;

/// A 2d size tagged with a unit.
define_matrix! {
    #[derive(RustcDecodable, RustcEncodable)]
    pub struct TypedSize2D<T, U> {
        pub width: T,
        pub height: T,
    }
}

/// Default 2d size type with no unit.
///
/// `Size2D` provides the same methods as `TypedSize2D`.
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;

impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}×{:?}", self.width, self.height)
    }
}

impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "({}x{})", self.width, self.height)
    }
}

impl<T, U> TypedSize2D<T, U> {
    /// Constructor taing scalar values.
    pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
        TypedSize2D {
            width: width,
            height: height,
            _unit: PhantomData,
        }
    }
}

impl<T: Clone, U> TypedSize2D<T, U> {
    /// Constructor taing scalar stronlgy typed lengths.
    pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(width.get(), height.get())
    }
}

impl<T: Round, U> TypedSize2D<T, U> {
    /// Rounds each component to the nearest integer value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn round(&self) -> Self {
        TypedSize2D::new(self.width.round(), self.height.round())
    }
}

impl<T: Ceil, U> TypedSize2D<T, U> {
    /// Rounds each component to the smallest integer equal or greater than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn ceil(&self) -> Self {
        TypedSize2D::new(self.height.ceil(), self.width.ceil())
    }
}

impl<T: Floor, U> TypedSize2D<T, U> {
    /// Rounds each component to the biggest integer equal or lower than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn floor(&self) -> Self {
        TypedSize2D::new(self.width.floor(), self.height.floor())
    }
}

impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
    pub fn area(&self) -> U { self.width * self.height }
}

impl<T: Zero, U> TypedSize2D<T, U> {
    pub fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Zero, U> Zero for TypedSize2D<T, U> {
    fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn mul(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width * scale, self.height * scale)
    }
}

impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn div(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width / scale, self.height / scale)
    }
}

impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
    type Output = TypedSize2D<T, U2>;
    #[inline]
    fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U2> {
        TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
    }
}

impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
    type Output = TypedSize2D<T, U1>;
    #[inline]
    fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U1> {
        TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
    }
}

impl<T: Copy, U> TypedSize2D<T, U> {
    /// Returns self.width as a Length carrying the unit.
    #[inline]
    pub fn width_typed(&self) -> Length<T, U> { Length::new(self.width) }

    /// Returns self.height as a Length carrying the unit.
    #[inline]
    pub fn height_typed(&self) -> Length<T, U> { Length::new(self.height) }

    #[inline]
    pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }

    /// Drop the units, preserving only the numeric value.
    pub fn to_untyped(&self) -> Size2D<T> {
        TypedSize2D::new(self.width, self.height)
    }

    /// Tag a unitless value with units.
    pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
        TypedSize2D::new(p.width, p.height)
    }
}

impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
    /// Cast from one numeric representation to another, preserving the units.
    ///
    /// When casting from floating point to integer coordinates, the decimals are truncated
    /// as one would expect from a simple cast, but this behavior does not always marke sense
    /// geometrically. Consider using round(), ceil or floor() before casting.
    pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
        match (NumCast::from(self.width), NumCast::from(self.height)) {
            (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
            _ => None
        }
    }

    // Convenience functions for common casts

    /// Cast into an f32 size.
    pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an usize size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_uint(&self) -> TypedSize2D<usize, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i32 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i64 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
        self.cast().unwrap()
    }
}
<commit_msg>Auto merge of #166 - mrobinson:size-arith, r=nox<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::UnknownUnit;
use length::Length;
use scale_factor::ScaleFactor;
use num::*;

use num_traits::NumCast;
use std::fmt;
use std::ops::{Add, Div, Mul, Sub};
use std::marker::PhantomData;

/// A 2d size tagged with a unit.
define_matrix! {
    #[derive(RustcDecodable, RustcEncodable)]
    pub struct TypedSize2D<T, U> {
        pub width: T,
        pub height: T,
    }
}

/// Default 2d size type with no unit.
///
/// `Size2D` provides the same methods as `TypedSize2D`.
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;

impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}×{:?}", self.width, self.height)
    }
}

impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "({}x{})", self.width, self.height)
    }
}

impl<T, U> TypedSize2D<T, U> {
    /// Constructor taing scalar values.
    pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
        TypedSize2D {
            width: width,
            height: height,
            _unit: PhantomData,
        }
    }
}

impl<T: Clone, U> TypedSize2D<T, U> {
    /// Constructor taing scalar stronlgy typed lengths.
    pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(width.get(), height.get())
    }
}

impl<T: Round, U> TypedSize2D<T, U> {
    /// Rounds each component to the nearest integer value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn round(&self) -> Self {
        TypedSize2D::new(self.width.round(), self.height.round())
    }
}

impl<T: Ceil, U> TypedSize2D<T, U> {
    /// Rounds each component to the smallest integer equal or greater than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn ceil(&self) -> Self {
        TypedSize2D::new(self.height.ceil(), self.width.ceil())
    }
}

impl<T: Floor, U> TypedSize2D<T, U> {
    /// Rounds each component to the biggest integer equal or lower than the orginal value.
    ///
    /// This behavior is preserved for negative values (unlike the basic cast).
    pub fn floor(&self) -> Self {
        TypedSize2D::new(self.width.floor(), self.height.floor())
    }
}

impl<T: Copy + Add<T, Output=T>, U> Add for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    fn add(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width + other.width, self.height + other.height)
    }
}

impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    fn sub(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width - other.width, self.height - other.height)
    }
}

impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
    pub fn area(&self) -> U { self.width * self.height }
}

impl<T: Zero, U> TypedSize2D<T, U> {
    pub fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Zero, U> Zero for TypedSize2D<T, U> {
    fn zero() -> TypedSize2D<T, U> {
        TypedSize2D::new(
            Zero::zero(),
            Zero::zero(),
        )
    }
}

impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn mul(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width * scale, self.height * scale)
    }
}

impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
    type Output = TypedSize2D<T, U>;
    #[inline]
    fn div(self, scale: T) -> TypedSize2D<T, U> {
        TypedSize2D::new(self.width / scale, self.height / scale)
    }
}

impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
    type Output = TypedSize2D<T, U2>;
    #[inline]
    fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U2> {
        TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
    }
}

impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
    type Output = TypedSize2D<T, U1>;
    #[inline]
    fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U1> {
        TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
    }
}

impl<T: Copy, U> TypedSize2D<T, U> {
    /// Returns self.width as a Length carrying the unit.
    #[inline]
    pub fn width_typed(&self) -> Length<T, U> { Length::new(self.width) }

    /// Returns self.height as a Length carrying the unit.
    #[inline]
    pub fn height_typed(&self) -> Length<T, U> { Length::new(self.height) }

    #[inline]
    pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }

    /// Drop the units, preserving only the numeric value.
    pub fn to_untyped(&self) -> Size2D<T> {
        TypedSize2D::new(self.width, self.height)
    }

    /// Tag a unitless value with units.
    pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
        TypedSize2D::new(p.width, p.height)
    }
}

impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
    /// Cast from one numeric representation to another, preserving the units.
    ///
    /// When casting from floating point to integer coordinates, the decimals are truncated
    /// as one would expect from a simple cast, but this behavior does not always marke sense
    /// geometrically. Consider using round(), ceil or floor() before casting.
    pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
        match (NumCast::from(self.width), NumCast::from(self.height)) {
            (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
            _ => None
        }
    }

    // Convenience functions for common casts

    /// Cast into an f32 size.
    pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an usize size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_uint(&self) -> TypedSize2D<usize, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i32 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
        self.cast().unwrap()
    }

    /// Cast into an i64 size, truncating decimals if any.
    ///
    /// When casting from floating point sizes, it is worth considering whether
    /// to round(), ceil() or floor() before the cast in order to obtain the desired
    /// conversion behavior.
    pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
        self.cast().unwrap()
    }
}

#[cfg(test)]
mod size2d {
    use super::Size2D;

    #[test]
    pub fn test_add() {
        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(3.0, 4.0);
        assert_eq!(p1 + p2, Size2D::new(4.0, 6.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 + p2, Size2D::new(1.0, 2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(-3.0, -4.0);
        assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0));

        let p1 = Size2D::new(0.0, 0.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 + p2, Size2D::new(0.0, 0.0));
    }

    #[test]
    pub fn test_sub() {
        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(3.0, 4.0);
        assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 - p2, Size2D::new(1.0, 2.0));

        let p1 = Size2D::new(1.0, 2.0);
        let p2 = Size2D::new(-3.0, -4.0);
        assert_eq!(p1 - p2, Size2D::new(4.0, 6.0));

        let p1 = Size2D::new(0.0, 0.0);
        let p2 = Size2D::new(0.0, 0.0);
        assert_eq!(p1 - p2, Size2D::new(0.0, 0.0));
    }
}
<|endoftext|>


<commit_before>//! Generates descriptors structure for unstable feature from Unstable Book

use crate::codegen::update;
use crate::codegen::{self, project_root, Mode, Result};
use crate::not_bash::{fs2, pushd, run};
use proc_macro2::TokenStream;
use quote::quote;
use std::path::PathBuf;
use walkdir::WalkDir;

fn generate_descriptor(src_dir: PathBuf) -> Result<TokenStream> {
    let files = WalkDir::new(src_dir.join("language-features"))
        .into_iter()
        .chain(WalkDir::new(src_dir.join("library-features")))
        .filter_map(|e| e.ok())
        .filter(|entry| {
            // Get all `.md ` files
            entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md"
        })
        .collect::<Vec<_>>();

    let definitions = files
        .iter()
        .map(|entry| {
            let path = entry.path();
            let feature_ident =
                format!("{}", path.file_stem().unwrap().to_str().unwrap().replace("-", "_"));
            let doc = format!("{}", std::fs::read_to_string(path).unwrap());

            quote! { LintCompletion { label: #feature_ident, description: #doc } }
        })
        .collect::<Vec<_>>();

    let ts = quote! {
        use crate::completion::LintCompletion;

        pub(crate) const UNSTABLE_FEATURE_DESCRIPTOR:  &[LintCompletion] = &[
            #(#definitions),*
        ];
    };
    Ok(ts)
}

pub fn generate_unstable_future_descriptor(mode: Mode) -> Result<()> {
    let path = project_root().join(codegen::STORAGE);
    fs2::create_dir_all(path.clone())?;

    let _d = pushd(path.clone());
    run!("git init")?;
    run!("git remote add -f origin {}", codegen::REPOSITORY_URL)?;
    run!("git pull origin master")?;

    let src_dir = path.join(codegen::REPO_PATH);
    let content = generate_descriptor(src_dir)?.to_string();

    let contents = crate::reformat(content)?;
    let destination = project_root().join(codegen::UNSTABLE_FEATURE);
    update(destination.as_path(), &contents, mode)?;

    Ok(())
}
<commit_msg>fix reformat<commit_after>//! Generates descriptors structure for unstable feature from Unstable Book

use crate::codegen::{update, reformat};
use crate::codegen::{self, project_root, Mode, Result};
use crate::not_bash::{fs2, pushd, run};
use proc_macro2::TokenStream;
use quote::quote;
use std::path::PathBuf;
use walkdir::WalkDir;

fn generate_descriptor(src_dir: PathBuf) -> Result<TokenStream> {
    let files = WalkDir::new(src_dir.join("language-features"))
        .into_iter()
        .chain(WalkDir::new(src_dir.join("library-features")))
        .filter_map(|e| e.ok())
        .filter(|entry| {
            // Get all `.md ` files
            entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md"
        })
        .collect::<Vec<_>>();

    let definitions = files
        .iter()
        .map(|entry| {
            let path = entry.path();
            let feature_ident =
                format!("{}", path.file_stem().unwrap().to_str().unwrap().replace("-", "_"));
            let doc = format!("{}", std::fs::read_to_string(path).unwrap());

            quote! { LintCompletion { label: #feature_ident, description: #doc } }
        })
        .collect::<Vec<_>>();

    let ts = quote! {
        use crate::completion::LintCompletion;

        pub(crate) const UNSTABLE_FEATURE_DESCRIPTOR:  &[LintCompletion] = &[
            #(#definitions),*
        ];
    };
    Ok(ts)
}

pub fn generate_unstable_future_descriptor(mode: Mode) -> Result<()> {
    let path = project_root().join(codegen::STORAGE);
    fs2::create_dir_all(path.clone())?;

    let _d = pushd(path.clone());
    run!("git init")?;
    run!("git remote add -f origin {}", codegen::REPOSITORY_URL)?;
    run!("git pull origin master")?;

    let src_dir = path.join(codegen::REPO_PATH);
    let content = generate_descriptor(src_dir)?.to_string();

    let contents = reformat(content)?;
    let destination = project_root().join(codegen::UNSTABLE_FEATURE);
    update(destination.as_path(), &contents, mode)?;

    Ok(())
}
<|endoftext|>


<commit_before>use super::with_heap;
use super::Value;
use super::HEAP_SIZE;
use std::rc::Rc;

/// Test that a Heap can at least allocate two objects.
#[test]
fn can_allocate_twice() {
    with_heap(|heap| {
        let obj1 = heap.alloc((Value::Int(1), Value::Null));
        let obj2 = heap.alloc((Value::Int(2), Value::Null));
        assert_eq!(obj1.head(), Value::Int(1));
        assert_eq!(obj2.head(), Value::Int(2));
    });
}

/// Test that rooted objects are not collected and reused.
#[test]
fn root_is_not_recycled() {
    with_heap(|heap| {
        // Create and root one object.
        let root = heap.alloc((Value::Int(1), Value::Str(Rc::new("hello world".to_string()))));

        // Subsequent allocations never return root.
        for _ in 0 .. HEAP_SIZE * 2 {
            let tmp = heap.alloc_null();
            assert!(tmp != root);
        }
    });
}

/// Test try_alloc()'s behavior when the heap is full and every Object is
/// reachable.
#[test]
fn full_heap() {
    with_heap(|heap| {
        // Fill up the heap by allocating HEAP_SIZE objects.
        let mut v = Value::Null;
        for _ in 0 .. HEAP_SIZE {
            v = Value::Pair(heap.alloc((Value::Null, v)));
        }

        // The whole heap is reachable.  Now try_alloc() should return null every
        // time it's called.
        for _ in 0 .. 4 {
            assert_eq!(heap.try_alloc((Value::Null, Value::Null)), None);
        }
    });
}

/// Test allocate()'s behavior when the heap is only almost full.
#[test]
fn nearly_full_heap() {
    with_heap(|heap| {
        // Make the heap nearly full by allocating (HEAP_SIZE - 1) objects.
        let mut v = Value::Null;
        for _ in 0 .. HEAP_SIZE - 1 {
            v = Value::Pair(heap.alloc((Value::Null, v)));
        }

        // Now the entire heap is reachable except for one Object.  We should
        // be able to call allocate() successfully, repeatedly.  It returns
        // that one object every time it's called!
        let last = heap.alloc_null().address();
        for _ in 0 .. 10 {
            assert_eq!(heap.alloc_null().address(), last);
        }
    });
}

/// Test that objects reachable from a root's `.head` or `.tail` are not
/// collected.
#[test]
fn reachable_objects_not_collected() {
    with_heap(|heap| {
        let obj1 = heap.alloc_null();
        let (p2, p3, p4, p5);
        {
            let obj2 = heap.alloc_null();
            p2 = obj2.address();
            obj1.set_head(Value::Pair(obj2.clone()));
            let obj3 = heap.alloc_null();
            p3 = obj3.address();
            obj1.set_tail(Value::Pair(obj3));
            let obj4 = heap.alloc_null();
            p4 = obj4.address();
            obj2.set_head(Value::Pair(obj4));
            let obj5 = heap.alloc_null();
            p5 = obj5.address();
            obj2.set_tail(Value::Pair(obj5));
        }

        heap.force_gc();

        let h = match obj1.head() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head")
        };
        assert_eq!(h.address(), p2);

        let t = match obj1.tail() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.tail")
        };
        assert_eq!(t.address(), p3);

        let hh = match h.head() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head.head")
        };
        assert_eq!(hh.address(), p4);

        let ht = match h.tail() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head.tail")
        };
        assert_eq!(ht.address(), p5);
    });
}

/// Test that the GC is not confused by an object that contains pointers to
/// itself.
#[test]
fn root_self_references() {
    with_heap(|heap| {
        // Create a root object that contains pointers to itself.
        let root = heap.alloc_null();
        root.set_head(Value::Pair(root.clone()));
        root.set_tail(Value::Pair(root.clone()));

        heap.force_gc();

        // After GC, the root object should be unchanged.
        assert_eq!(root.head(), Value::Pair(root.clone()));
        assert_eq!(root.tail(), Value::Pair(root.clone()));
    });
}

/// Test that the GC is not confused by cycles in the reachable object graph.
#[test]
fn test_root_cycle() {
    with_heap(|heap| {
        // Set up obj1 and obj2 to point to each other.
        let obj1 = heap.alloc_null();
        let obj2 = heap.alloc((Value::Pair(obj1.clone()),
                               Value::Pair(obj1.clone())));  // obj2 points to obj1
        obj1.set_head(Value::Pair(obj2.clone()));  // and vice versa
        obj1.set_tail(Value::Pair(obj2.clone()));

        heap.force_gc();

        // After GC, the two objects are unchanged.
        assert_eq!(obj1.head(), Value::Pair(obj2.clone()));
        assert_eq!(obj1.tail(), Value::Pair(obj2.clone()));
        assert_eq!(obj2.head(), Value::Pair(obj1.clone()));
        assert_eq!(obj2.tail(), Value::Pair(obj1.clone()));
    });
}

/// Test that the GC is not confused by cycles that are garbage.
#[test]
fn unreachable_cycle() {
    with_heap(|heap| {
        // Make a cycle.
        let (p1, p2);
        {
            let obj1 = heap.alloc_null();
            p1 = obj1.address();
            let obj2 = heap.alloc_null();
            p2 = obj2.address();
            obj2.set_tail(Value::Pair(obj1.clone()));
            obj1.set_tail(Value::Pair(obj2.clone()));
        }  // Make the cycle unreachable.

        // Allocation should eventually recycle both objects.
        let mut recycled1 = 0;
        let mut recycled2 = 0;
        let mut root = Value::Null;
        for _ in 0 .. HEAP_SIZE {
            let p = heap.alloc((Value::Null, root));
            root = Value::Pair(p.clone());
            if p.address() == p1 {
                recycled1 += 1;
            }
            if p.address() == p2 {
                recycled2 += 1;
            }
        }
        assert_eq!(recycled1, 1);
        assert_eq!(recycled2, 1);
    });
}

// Tests that you can't share values across separate heaps. Amazingly,
// Rust enforces this rule at compile time; unfortunately, I don't fully
// understand *all* the parts of the enforcement mechanism, so it's worth
// testing.
//
// Try uncommenting the tests below: if any of them compile successfully,
// consider it a test failure!
/*

/// The most obvious case: you can't just straight-up pass an object from
/// one heap to another heap's `alloc` method.
#[test]
fn bug_cross_heap_edges_1() {
    with_heap(|heap1| {
        with_heap(|heap2| {
            let obj1 = heap1.alloc_null();  // error: cannot infer an appropriate lifetime
            let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
        });
    });
}

/// `with_heap` callers can't smuggle the heap out of the lifetime of the
/// closure callback. (I am not sure but I think it would be unsafe if you
/// could.)
#[test]
fn bug_cross_heap_edges_2() {
    let mut heap1 = with_heap(|h| *h);  // error: cannot infer an appropriate lifetime
    let mut heap2 = with_heap(|h| *h);
    let obj1 = heap1.alloc_null();
    let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
}

#[test]
fn bug_cross_heap_edges_2a() {
    with_heap(|heap1| {
        let mut heap2 = with_heap(|h| *h);  // error: cannot infer an appropriate lifetime
        let obj1 = heap1.alloc_null();
        let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
    });
}

/// Being able to swap two heap bindings would utterly break the safety
/// enforcement regime. :)
#[test]
fn bug_cross_heap_edges_3() {
    use std;
    with_heap(|mut heap1| {
        with_heap(|mut heap2| {
            let obj1 = heap1.alloc_null();
            std::mem::swap(&mut heap1, &mut heap2);  // error: mismatched types ... (lifetime mismatch)
            let obj2 = heap1.alloc((Value::Null, Value::Pair(obj1)));
        });
    });
}

#[test]
fn bug_heap_dropping() {
    // Check that Rust does not let us drop the heap while references into it
    // still exist (they would be dangling pointers). The first iteration of
    // `with_heap` passed the heap to the callback by value, and thus was
    // susceptible to this bug! It caused:
    //
    //     thread panicked while panicking. aborting.
    //
    // The fix was to pass the heap to the closure by reference.
    //
    with_heap(|heap| {
        let obj = heap.alloc_null();

        // note: dropping `heap` would just drop the reference, which is no problem
        ::std::mem::drop(*heap);  // error: cannot move out of borrowed content

        let val = Value::Pair(obj.clone());
        obj.set_head(val); // occurs after the heap is gone
    });
}
*/

<commit_msg>Two more tests of clever get-a-dangling-heap-pointer schemes foiled by the `for<'a>` trick. `\o/`<commit_after>use super::with_heap;
use super::Value;
use super::HEAP_SIZE;
use std::rc::Rc;

/// Test that a Heap can at least allocate two objects.
#[test]
fn can_allocate_twice() {
    with_heap(|heap| {
        let obj1 = heap.alloc((Value::Int(1), Value::Null));
        let obj2 = heap.alloc((Value::Int(2), Value::Null));
        assert_eq!(obj1.head(), Value::Int(1));
        assert_eq!(obj2.head(), Value::Int(2));
    });
}

/// Test that rooted objects are not collected and reused.
#[test]
fn root_is_not_recycled() {
    with_heap(|heap| {
        // Create and root one object.
        let root = heap.alloc((Value::Int(1), Value::Str(Rc::new("hello world".to_string()))));

        // Subsequent allocations never return root.
        for _ in 0 .. HEAP_SIZE * 2 {
            let tmp = heap.alloc_null();
            assert!(tmp != root);
        }
    });
}

/// Test try_alloc()'s behavior when the heap is full and every Object is
/// reachable.
#[test]
fn full_heap() {
    with_heap(|heap| {
        // Fill up the heap by allocating HEAP_SIZE objects.
        let mut v = Value::Null;
        for _ in 0 .. HEAP_SIZE {
            v = Value::Pair(heap.alloc((Value::Null, v)));
        }

        // The whole heap is reachable.  Now try_alloc() should return null every
        // time it's called.
        for _ in 0 .. 4 {
            assert_eq!(heap.try_alloc((Value::Null, Value::Null)), None);
        }
    });
}

/// Test allocate()'s behavior when the heap is only almost full.
#[test]
fn nearly_full_heap() {
    with_heap(|heap| {
        // Make the heap nearly full by allocating (HEAP_SIZE - 1) objects.
        let mut v = Value::Null;
        for _ in 0 .. HEAP_SIZE - 1 {
            v = Value::Pair(heap.alloc((Value::Null, v)));
        }

        // Now the entire heap is reachable except for one Object.  We should
        // be able to call allocate() successfully, repeatedly.  It returns
        // that one object every time it's called!
        let last = heap.alloc_null().address();
        for _ in 0 .. 10 {
            assert_eq!(heap.alloc_null().address(), last);
        }
    });
}

/// Test that objects reachable from a root's `.head` or `.tail` are not
/// collected.
#[test]
fn reachable_objects_not_collected() {
    with_heap(|heap| {
        let obj1 = heap.alloc_null();
        let (p2, p3, p4, p5);
        {
            let obj2 = heap.alloc_null();
            p2 = obj2.address();
            obj1.set_head(Value::Pair(obj2.clone()));
            let obj3 = heap.alloc_null();
            p3 = obj3.address();
            obj1.set_tail(Value::Pair(obj3));
            let obj4 = heap.alloc_null();
            p4 = obj4.address();
            obj2.set_head(Value::Pair(obj4));
            let obj5 = heap.alloc_null();
            p5 = obj5.address();
            obj2.set_tail(Value::Pair(obj5));
        }

        heap.force_gc();

        let h = match obj1.head() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head")
        };
        assert_eq!(h.address(), p2);

        let t = match obj1.tail() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.tail")
        };
        assert_eq!(t.address(), p3);

        let hh = match h.head() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head.head")
        };
        assert_eq!(hh.address(), p4);

        let ht = match h.tail() {
            Value::Pair(p) => p,
            _ => panic!("expected pair in obj1.head.tail")
        };
        assert_eq!(ht.address(), p5);
    });
}

/// Test that the GC is not confused by an object that contains pointers to
/// itself.
#[test]
fn root_self_references() {
    with_heap(|heap| {
        // Create a root object that contains pointers to itself.
        let root = heap.alloc_null();
        root.set_head(Value::Pair(root.clone()));
        root.set_tail(Value::Pair(root.clone()));

        heap.force_gc();

        // After GC, the root object should be unchanged.
        assert_eq!(root.head(), Value::Pair(root.clone()));
        assert_eq!(root.tail(), Value::Pair(root.clone()));
    });
}

/// Test that the GC is not confused by cycles in the reachable object graph.
#[test]
fn test_root_cycle() {
    with_heap(|heap| {
        // Set up obj1 and obj2 to point to each other.
        let obj1 = heap.alloc_null();
        let obj2 = heap.alloc((Value::Pair(obj1.clone()),
                               Value::Pair(obj1.clone())));  // obj2 points to obj1
        obj1.set_head(Value::Pair(obj2.clone()));  // and vice versa
        obj1.set_tail(Value::Pair(obj2.clone()));

        heap.force_gc();

        // After GC, the two objects are unchanged.
        assert_eq!(obj1.head(), Value::Pair(obj2.clone()));
        assert_eq!(obj1.tail(), Value::Pair(obj2.clone()));
        assert_eq!(obj2.head(), Value::Pair(obj1.clone()));
        assert_eq!(obj2.tail(), Value::Pair(obj1.clone()));
    });
}

/// Test that the GC is not confused by cycles that are garbage.
#[test]
fn unreachable_cycle() {
    with_heap(|heap| {
        // Make a cycle.
        let (p1, p2);
        {
            let obj1 = heap.alloc_null();
            p1 = obj1.address();
            let obj2 = heap.alloc_null();
            p2 = obj2.address();
            obj2.set_tail(Value::Pair(obj1.clone()));
            obj1.set_tail(Value::Pair(obj2.clone()));
        }  // Make the cycle unreachable.

        // Allocation should eventually recycle both objects.
        let mut recycled1 = 0;
        let mut recycled2 = 0;
        let mut root = Value::Null;
        for _ in 0 .. HEAP_SIZE {
            let p = heap.alloc((Value::Null, root));
            root = Value::Pair(p.clone());
            if p.address() == p1 {
                recycled1 += 1;
            }
            if p.address() == p2 {
                recycled2 += 1;
            }
        }
        assert_eq!(recycled1, 1);
        assert_eq!(recycled2, 1);
    });
}

// Tests that you can't share values across separate heaps. Amazingly,
// Rust enforces this rule at compile time; unfortunately, I don't fully
// understand *all* the parts of the enforcement mechanism, so it's worth
// testing.
//
// Try uncommenting the tests below: if any of them compile successfully,
// consider it a test failure!
/*

/// The most obvious case: you can't just straight-up pass an object from
/// one heap to another heap's `alloc` method.
#[test]
fn bug_cross_heap_edges_1() {
    with_heap(|heap1| {
        with_heap(|heap2| {
            let obj1 = heap1.alloc_null();  // error: cannot infer an appropriate lifetime
            let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
        });
    });
}

/// `with_heap` callers can't smuggle the heap out of the lifetime of the
/// closure callback. (I am not sure but I think it would be unsafe if you
/// could.)
#[test]
fn bug_cross_heap_edges_2() {
    let mut heap1 = with_heap(|h| *h);  // error: cannot infer an appropriate lifetime
    let mut heap2 = with_heap(|h| *h);
    let obj1 = heap1.alloc_null();
    let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
}

#[test]
fn bug_cross_heap_edges_2a() {
    with_heap(|heap1| {
        let mut heap2 = with_heap(|h| *h);  // error: cannot infer an appropriate lifetime
        let obj1 = heap1.alloc_null();
        let obj2 = heap2.alloc((Value::Null, Value::Pair(obj1)));
    });
}

/// Being able to swap two heap bindings would utterly break the safety
/// enforcement regime. :)
#[test]
fn bug_cross_heap_edges_3() {
    use std;
    with_heap(|mut heap1| {
        with_heap(|mut heap2| {
            let obj1 = heap1.alloc_null();
            std::mem::swap(&mut heap1, &mut heap2);  // error: mismatched types ... (lifetime mismatch)
            let obj2 = heap1.alloc((Value::Null, Value::Pair(obj1)));
        });
    });
}

#[test]
fn bug_heap_dropping() {
    // Check that Rust does not let us drop the heap while references into it
    // still exist (they would be dangling pointers). The first iteration of
    // `with_heap` passed the heap to the callback by value, and thus was
    // susceptible to this bug! It caused:
    //
    //     thread panicked while panicking. aborting.
    //
    // The fix was to pass the heap to the closure by reference.
    //
    with_heap(|heap| {
        let obj = heap.alloc_null();

        // note: dropping `heap` would just drop the reference, which is no problem
        ::std::mem::drop(*heap);  // error: cannot move out of borrowed content

        let val = Value::Pair(obj.clone());
        obj.set_head(val); // occurs after the heap is gone
    });
}

#[test]
fn bug_outliving_1() {
    let obj = with_heap(|heap| {
        heap.alloc_null()  // error: cannot infer an appropriate lifetime
    });

    let val = Value::Pair(obj.clone());
    obj.set_head(val);  // occurs after the heap is gone
}

#[test]
fn bug_outliving_2() {
    let obj;
    with_heap(|heap| {
        obj = heap.alloc_null();  // error: cannot infer an appropriate lifetime
    });

    let val = Value::Pair(obj.clone());
    obj.set_head(val);  // occurs after the heap is gone
}

*/

<|endoftext|>


<commit_before>mod skills {
    use skills;
    use path;

    #[test]
    fn test_skills() {
        match ::skills::SkillReader(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                die!(msg);
            },
            result::Ok(skill_reader) => {
                match skill_reader.read_skill(0) {
                    option::Some(skill) => {
                        assert skill.name == ~"Alchemy";
                        assert skill.clickable == false;
                    },
                    option::None => {
                        die!(~"No skill at 0");
                    }
                };
                //There should be a skill at #10
                match skill_reader.read_skill(10) {
                    option::Some(_skill) => {
                        ()
                    },
                    option::None => {
                        die!(~"No skill at 10");
                    }
                };
                //There shouldn't be 100 skills
                match skill_reader.read_skill(100) {
                    option::Some(_skill) => {
                        die!(~"Skill at 100");
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_all() {
        match ::skills::load_skills(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                die!(msg);
            },
            result::Ok(skill_list) => {
                assert skill_list.len() >= 49 //49 skills in the original UO
            }
        }
    }
}

mod map {
    use map;
    use path;

    #[test]
    fn test_read_map_statics() {
        match ::map::StaticReader(&path::Path(~"files/staidx0.mul"), &path::Path(~"files/statics0.mul")) {
            result::Err(msg) => {
                die!(msg);
            },
            result::Ok(staticReader) => {
                match staticReader.read_block(0) {
                    option::None => {
                        match staticReader.read_block(200 + (200 * 512)) {
                            option::Some(Statics) => {
                                assert(Statics.len() > 0);
                            },
                            option::None => {
                                die!(~"Expected tile at 200x200 but found none");
                            }
                        }
                    },
                    option::Some(_) => {
                        die!(~"Found unexpected statics at 0");
                    }
                }
            }
        }
    }
}

mod art {
    use art;
    use art::Tile;
    use path;

    #[test]
    fn test_read_tile() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                die!(msg);
            },
            result::Ok(tileReader) => {
                match tileReader.read_tile(100) {
                    option::Some(tile) => {
                        assert tile.raw_image.len() == 1012;
                        let bitmap = tile.with_transparency(0xF000);
                        assert bitmap.len() == (44*44);
                        assert bitmap[0] == 0xF000;
                        assert bitmap[21] != 0xF000;
                    },
                    option::None => {
                        die!(~"Couldn't read tile 100")
                    }
                };
                match tileReader.read_tile(0x4000) {
                    option::Some(_tile) => {
                        die!(~"Read static 0x4000 as tile")
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_static() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                die!(msg);
            },
            result::Ok(tile_reader) => {
                match tile_reader.read_static(0x4000) {
                    option::Some(static_tile) => {
                        let bitmap = static_tile.with_transparency(0xF000);
                        //warn!("%u, %u, %u", bitmap.len(), static_tile.width as uint, static_tile.height as uint);
                        //assert bitmap.len() == (static_tile.width + static_tile.height) as uint;
                    },
                    option::None => {
                        die!(~"Couldn't read tile 0x4000");
                    }
                };
            }
        }
    }
}
<commit_msg>Switched Die to Fail<commit_after>mod skills {
    use skills;
    use path;

    #[test]
    fn test_skills() {
        match ::skills::SkillReader(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_reader) => {
                match skill_reader.read_skill(0) {
                    option::Some(skill) => {
                        assert skill.name == ~"Alchemy";
                        assert skill.clickable == false;
                    },
                    option::None => {
                        fail!(~"No skill at 0");
                    }
                };
                //There should be a skill at #10
                match skill_reader.read_skill(10) {
                    option::Some(_skill) => {
                        ()
                    },
                    option::None => {
                        fail!(~"No skill at 10");
                    }
                };
                //There shouldn't be 100 skills
                match skill_reader.read_skill(100) {
                    option::Some(_skill) => {
                        fail!(~"Skill at 100");
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_all() {
        match ::skills::load_skills(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_list) => {
                assert skill_list.len() >= 49 //49 skills in the original UO
            }
        }
    }
}

mod map {
    use map;
    use path;

    #[test]
    fn test_read_map_statics() {
        match ::map::StaticReader(&path::Path(~"files/staidx0.mul"), &path::Path(~"files/statics0.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(staticReader) => {
                match staticReader.read_block(0) {
                    option::None => {
                        match staticReader.read_block(200 + (200 * 512)) {
                            option::Some(Statics) => {
                                assert(Statics.len() > 0);
                            },
                            option::None => {
                                fail!(~"Expected tile at 200x200 but found none");
                            }
                        }
                    },
                    option::Some(_) => {
                        fail!(~"Found unexpected statics at 0");
                    }
                }
            }
        }
    }
}

mod art {
    use art;
    use art::Tile;
    use path;

    #[test]
    fn test_read_tile() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tileReader) => {
                match tileReader.read_tile(100) {
                    option::Some(tile) => {
                        assert tile.raw_image.len() == 1012;
                        let bitmap = tile.with_transparency(0xF000);
                        assert bitmap.len() == (44*44);
                        assert bitmap[0] == 0xF000;
                        assert bitmap[21] != 0xF000;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 100")
                    }
                };
                match tileReader.read_tile(0x4000) {
                    option::Some(_tile) => {
                        fail!(~"Read static 0x4000 as tile")
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_static() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_reader) => {
                match tile_reader.read_static(0x4000) {
                    option::Some(static_tile) => {
                        let bitmap = static_tile.with_transparency(0xF000);
                        //warn!("%u, %u, %u", bitmap.len(), static_tile.width as uint, static_tile.height as uint);
                        //assert bitmap.len() == (static_tile.width + static_tile.height) as uint;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 0x4000");
                    }
                };
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate html2text;
use std::io;

fn main() {
    let stdin = io::stdin();

    println!("{}", html2text::from_read(&mut stdin.lock(), 12));
}<commit_msg>Put html2txt's example width back to 80.<commit_after>extern crate html2text;
use std::io;

fn main() {
    let stdin = io::stdin();

    println!("{}", html2text::from_read(&mut stdin.lock(), 80));
}<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * kit/kernel/collections/treemap.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! A TreeMap implementation based on [AA
//! trees](http://en.wikipedia.org/wiki/AA_tree).

use core::prelude::*;
use core::mem::{swap, replace};
use core::cmp;
use core::cmp::Ordering::*;
use core::fmt;

use memory::Box;

pub struct TreeMap<K, V> {
    root: Option<Box<Node<K, V>>>,
}

impl<K: Ord, V> TreeMap<K, V> {
    /// Create a new empty `TreeMap`.
    pub fn new() -> TreeMap<K, V> {
        TreeMap { root: None }
    }

    /// Get a reference to the value corresponding to the given key, if found.
    pub fn lookup<'a>(&'a self, key: &K) -> Option<&'a V> {
        lookup(&self.root, key)
    }

    /// Get a mutable reference to the value corresponding to the given key, if
    /// found.
    pub fn lookup_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V> {
        lookup_mut(&mut self.root, key)
    }

    /// Insert a key and value into the `TreeMap`.
    ///
    /// Replaces any pre-existing value and returns the original value, if
    /// present.
    pub fn insert(&mut self, key: K, val: V) -> Option<V> {
        insert(&mut self.root, key, val)
    }

    /// Delete a key from a `TreeMap`. If found, returns the removed value.
    pub fn delete(&mut self, key: &K) -> Option<V> {
        delete(&mut self.root, key)
    }
}

impl<K, V> fmt::Debug for TreeMap<K, V>
        where K: fmt::Debug, V: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TreeMap {{ root: {:?} }}", self.root)
    }
}

// 1. The level of every leaf node is one.
// 2. The level of every left child is exactly one less than that of its parent.
// 3. The level of every right child is equal to or one less than that of its
//    parent.
// 4. The level of every right grandchild is strictly less than that of its
//    grandparent.
// 5. Every node of level greater than one has two children.

struct Node<K, V> {
    key:   K,
    val:   V,
    level: usize,
    left:  Option<Box<Node<K, V>>>,
    right: Option<Box<Node<K, V>>>,
}

impl<K, V> Node<K, V> {
    fn new(key: K, val: V) -> Node<K, V> {
        Node {
            key:   key,
            val:   val,
            level: 1,
            left:  None,
            right: None,
        }
    }
}

fn lookup<'a, K: Ord, V>(t: &'a Option<Box<Node<K, V>>>, key: &K)
          -> Option<&'a V> {

    t.as_ref().and_then(|node| match key.cmp(&node.key) {
        Less    => lookup(&node.left, key),
        Greater => lookup(&node.right, key),
        Equal   => Some(&node.val)
    })
}

fn lookup_mut<'a, K: Ord, V>(t: &'a mut Option<Box<Node<K, V>>>, key: &K)
              -> Option<&'a mut V> {

    t.as_mut().and_then(|node| match key.cmp(&node.key) {
        Less    => lookup_mut(&mut node.left, key),
        Greater => lookup_mut(&mut node.right, key),
        Equal   => Some(&mut node.val)
    })
}

/// Right rotation to replace a subtree contaning a left horizontal link with one
/// containing a right horizontal link instead.
fn skew<K, V>(t: &mut Box<Node<K, V>>) {
    if t.left.as_ref().map_or(false, |x| x.level == t.level) {
        // Swap the pointers of horizontal left links.
        let mut l = t.left.take().unwrap();
        swap(&mut t.left, &mut l.right);
        swap(t, &mut l);
        t.right = Some(l);
    }
}

/// Left rotation and level increase to replace a subtree containing two or more
/// consecutive right links with one containing two fewer consecutive right
/// horizontal links.
fn split<K, V>(t: &mut Box<Node<K, V>>) {
    if t.right.as_ref().map_or(false,
            |x| x.right.as_ref().map_or(false, |y| y.level == t.level)) {

        // We have two horizontal right links. Take the middle node, elevate it,
        // and return it.
        let mut r = t.right.take().unwrap();
        swap(&mut t.right, &mut r.left);
        swap(t, &mut r);
        t.level += 1;
        t.left = Some(r);
    }
}

fn insert<K: Ord, V>(t: &mut Option<Box<Node<K, V>>>, key: K, val: V)
          -> Option<V> {

    match *t {
        Some(ref mut node) => match key.cmp(&node.key) {
            Less => {
                let r = insert(&mut node.left, key, val);
                skew(node);
                split(node);
                r
            },

            Greater => {
                let r = insert(&mut node.right, key, val);
                skew(node);
                split(node);
                r
            },

            Equal => Some(replace(&mut node.val, val))
        },

        None => {
            *t = Some(box Node::new(key, val));
            None
        }
    }
}

fn take_closest<K, V>(node: &mut Node<K, V>) -> Option<Box<Node<K, V>>> {
    fn leftmost<K, V>(t: &mut Option<Box<Node<K, V>>>) -> Box<Node<K, V>> {
        if t.as_ref().unwrap().left.is_some() {
            leftmost(&mut t.as_mut().unwrap().left)
        } else {
            t.take().unwrap()
        }
    }

    fn rightmost<K, V>(t: &mut Option<Box<Node<K, V>>>) -> Box<Node<K, V>> {
        if t.as_ref().unwrap().right.is_some() {
            rightmost(&mut t.as_mut().unwrap().right)
        } else {
            t.take().unwrap()
        }
    }

    if node.left.is_some() {
        Some(rightmost(&mut node.left))
    } else if node.right.is_some() {
        Some(leftmost(&mut node.right))
    } else {
        None
    }
}

fn decrease_level<K, V>(node: &mut Node<K, V>) {
    fn level<K, V>(t: &Option<Box<Node<K, V>>>) -> usize {
        t.as_ref().map(|x| x.level).unwrap_or(0)
    }

    let should_be = cmp::min(level(&node.left), level(&node.right)) + 1;

    if should_be < node.level {
        node.level = should_be;

        node.right.as_mut().map(|r| {
            if should_be < r.level {
                r.level = should_be;
            }
        });
    }
}

fn delete<K: Ord, V>(t: &mut Option<Box<Node<K, V>>>, key: &K) -> Option<V> {
    t.take().and_then(|mut node| {
        let r = match key.cmp(&node.key) {
            Less    => delete(&mut node.left, key),
            Greater => delete(&mut node.right, key),
            Equal   => match take_closest(&mut node) {
                Some(c) => {
                    let Node { key: k, val: v, .. } = c.unwrap();
                    node.key = k;
                    Some(replace(&mut node.val, v))
                },
                None => return Some(node.unwrap().val) // Leaf
            }
        };

        // Rebalance the tree. Decrease the level of all nodes in this level if
        // necessary, and then skew and split all nodes in the new level.
        decrease_level(&mut node);
        skew(&mut node);
        node.right.as_mut().map(|right| {
            skew(right);
            right.right.as_mut().map(|r_right| skew(r_right));
        });
        split(&mut node);
        node.right.as_mut().map(|right| split(right));

        *t = Some(node);
        r
    })
}

impl<K, V> fmt::Debug for Node<K, V>
        where K: fmt::Debug, V: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Node {{ key: {:?}, val: {:?}, level: {:?}, \
                left: {:?}, right: {:?} }}",
               self.key, self.val, self.level, self.left, self.right)
    }
}
<commit_msg>[kernel::collections::treemap] add len()<commit_after>/*******************************************************************************
 *
 * kit/kernel/collections/treemap.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

//! A TreeMap implementation based on [AA
//! trees](http://en.wikipedia.org/wiki/AA_tree).

use core::prelude::*;
use core::mem::{swap, replace};
use core::cmp;
use core::cmp::Ordering::*;
use core::fmt;

use memory::Box;

pub struct TreeMap<K, V> {
    root: Option<Box<Node<K, V>>>,
    len:  usize,
}

impl<K: Ord, V> TreeMap<K, V> {
    /// Create a new empty `TreeMap`.
    pub fn new() -> TreeMap<K, V> {
        TreeMap { root: None, len: 0 }
    }

    /// Get a reference to the value corresponding to the given key, if found.
    pub fn lookup<'a>(&'a self, key: &K) -> Option<&'a V> {
        lookup(&self.root, key)
    }

    /// Get a mutable reference to the value corresponding to the given key, if
    /// found.
    pub fn lookup_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V> {
        lookup_mut(&mut self.root, key)
    }

    /// Insert a key and value into the `TreeMap`.
    ///
    /// Replaces any pre-existing value and returns the original value, if
    /// present.
    pub fn insert(&mut self, key: K, val: V) -> Option<V> {
        let r = insert(&mut self.root, key, val);

        if r.is_none() {
            self.len += 1;
        }

        r
    }

    /// Delete a key from a `TreeMap`. If found, returns the removed value.
    pub fn delete(&mut self, key: &K) -> Option<V> {
        let r = delete(&mut self.root, key);

        if r.is_some() {
            self.len -= 1;
        }

        r
    }

    /// The number of nodes in the `TreeMap`.
    pub fn len(&self) -> usize {
        self.len
    }
}

impl<K, V> fmt::Debug for TreeMap<K, V>
        where K: fmt::Debug, V: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TreeMap {{ root: {:?} }}", self.root)
    }
}

// 1. The level of every leaf node is one.
// 2. The level of every left child is exactly one less than that of its parent.
// 3. The level of every right child is equal to or one less than that of its
//    parent.
// 4. The level of every right grandchild is strictly less than that of its
//    grandparent.
// 5. Every node of level greater than one has two children.

struct Node<K, V> {
    key:   K,
    val:   V,
    level: usize,
    left:  Option<Box<Node<K, V>>>,
    right: Option<Box<Node<K, V>>>,
}

impl<K, V> Node<K, V> {
    fn new(key: K, val: V) -> Node<K, V> {
        Node {
            key:   key,
            val:   val,
            level: 1,
            left:  None,
            right: None,
        }
    }
}

fn lookup<'a, K: Ord, V>(t: &'a Option<Box<Node<K, V>>>, key: &K)
          -> Option<&'a V> {

    t.as_ref().and_then(|node| match key.cmp(&node.key) {
        Less    => lookup(&node.left, key),
        Greater => lookup(&node.right, key),
        Equal   => Some(&node.val)
    })
}

fn lookup_mut<'a, K: Ord, V>(t: &'a mut Option<Box<Node<K, V>>>, key: &K)
              -> Option<&'a mut V> {

    t.as_mut().and_then(|node| match key.cmp(&node.key) {
        Less    => lookup_mut(&mut node.left, key),
        Greater => lookup_mut(&mut node.right, key),
        Equal   => Some(&mut node.val)
    })
}

/// Right rotation to replace a subtree contaning a left horizontal link with one
/// containing a right horizontal link instead.
fn skew<K, V>(t: &mut Box<Node<K, V>>) {
    if t.left.as_ref().map_or(false, |x| x.level == t.level) {
        // Swap the pointers of horizontal left links.
        let mut l = t.left.take().unwrap();
        swap(&mut t.left, &mut l.right);
        swap(t, &mut l);
        t.right = Some(l);
    }
}

/// Left rotation and level increase to replace a subtree containing two or more
/// consecutive right links with one containing two fewer consecutive right
/// horizontal links.
fn split<K, V>(t: &mut Box<Node<K, V>>) {
    if t.right.as_ref().map_or(false,
            |x| x.right.as_ref().map_or(false, |y| y.level == t.level)) {

        // We have two horizontal right links. Take the middle node, elevate it,
        // and return it.
        let mut r = t.right.take().unwrap();
        swap(&mut t.right, &mut r.left);
        swap(t, &mut r);
        t.level += 1;
        t.left = Some(r);
    }
}

fn insert<K: Ord, V>(t: &mut Option<Box<Node<K, V>>>, key: K, val: V)
          -> Option<V> {

    match *t {
        Some(ref mut node) => match key.cmp(&node.key) {
            Less => {
                let r = insert(&mut node.left, key, val);
                skew(node);
                split(node);
                r
            },

            Greater => {
                let r = insert(&mut node.right, key, val);
                skew(node);
                split(node);
                r
            },

            Equal => Some(replace(&mut node.val, val))
        },

        None => {
            *t = Some(box Node::new(key, val));
            None
        }
    }
}

fn take_closest<K, V>(node: &mut Node<K, V>) -> Option<Box<Node<K, V>>> {
    fn leftmost<K, V>(t: &mut Option<Box<Node<K, V>>>) -> Box<Node<K, V>> {
        if t.as_ref().unwrap().left.is_some() {
            leftmost(&mut t.as_mut().unwrap().left)
        } else {
            t.take().unwrap()
        }
    }

    fn rightmost<K, V>(t: &mut Option<Box<Node<K, V>>>) -> Box<Node<K, V>> {
        if t.as_ref().unwrap().right.is_some() {
            rightmost(&mut t.as_mut().unwrap().right)
        } else {
            t.take().unwrap()
        }
    }

    if node.left.is_some() {
        Some(rightmost(&mut node.left))
    } else if node.right.is_some() {
        Some(leftmost(&mut node.right))
    } else {
        None
    }
}

fn decrease_level<K, V>(node: &mut Node<K, V>) {
    fn level<K, V>(t: &Option<Box<Node<K, V>>>) -> usize {
        t.as_ref().map(|x| x.level).unwrap_or(0)
    }

    let should_be = cmp::min(level(&node.left), level(&node.right)) + 1;

    if should_be < node.level {
        node.level = should_be;

        node.right.as_mut().map(|r| {
            if should_be < r.level {
                r.level = should_be;
            }
        });
    }
}

fn delete<K: Ord, V>(t: &mut Option<Box<Node<K, V>>>, key: &K) -> Option<V> {
    t.take().and_then(|mut node| {
        let r = match key.cmp(&node.key) {
            Less    => delete(&mut node.left, key),
            Greater => delete(&mut node.right, key),
            Equal   => match take_closest(&mut node) {
                Some(c) => {
                    let Node { key: k, val: v, .. } = c.unwrap();
                    node.key = k;
                    Some(replace(&mut node.val, v))
                },
                None => return Some(node.unwrap().val) // Leaf
            }
        };

        // Rebalance the tree. Decrease the level of all nodes in this level if
        // necessary, and then skew and split all nodes in the new level.
        decrease_level(&mut node);
        skew(&mut node);
        node.right.as_mut().map(|right| {
            skew(right);
            right.right.as_mut().map(|r_right| skew(r_right));
        });
        split(&mut node);
        node.right.as_mut().map(|right| split(right));

        *t = Some(node);
        r
    })
}

impl<K, V> fmt::Debug for Node<K, V>
        where K: fmt::Debug, V: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Node {{ key: {:?}, val: {:?}, level: {:?}, \
                left: {:?}, right: {:?} }}",
               self.key, self.val, self.level, self.left, self.right)
    }
}
<|endoftext|>