<commit_before>/**
 * Concurrency-enabled mechanisms for sharing mutable and/or immutable state
 * between tasks.
 */

import unsafe::{shared_mutable_state, clone_shared_mutable_state,
                get_shared_mutable_state, get_shared_immutable_state};
import sync::{condvar, mutex, rwlock};

export arc, clone, get;
export mutex_arc, rw_arc;

/****************************************************************************
 * Immutable ARC
 ****************************************************************************/

/// An atomically reference counted wrapper for shared immutable state.
struct arc<T: const send> { x: shared_mutable_state<T>; }

/// Create an atomically reference counted wrapper.
fn arc<T: const send>(+data: T) -> arc<T> {
    arc { x: unsafe { shared_mutable_state(data) } }
}

/**
 * Access the underlying data in an atomically reference counted
 * wrapper.
 */
fn get<T: const send>(rc: &arc<T>) -> &T {
    unsafe { get_shared_immutable_state(&rc.x) }
}

/**
 * Duplicate an atomically reference counted wrapper.
 *
 * The resulting two `arc` objects will point to the same underlying data
 * object. However, one of the `arc` objects can be sent to another task,
 * allowing them to share the underlying data.
 */
fn clone<T: const send>(rc: &arc<T>) -> arc<T> {
    arc { x: unsafe { clone_shared_mutable_state(&rc.x) } }
}

/****************************************************************************
 * Mutex protected ARC (unsafe)
 ****************************************************************************/

struct mutex_arc_inner<T: send> { lock: mutex; failed: bool; data: T; }
/// An ARC with mutable data protected by a blocking mutex.
struct mutex_arc<T: send> { x: shared_mutable_state<mutex_arc_inner<T>>; }

/// Create a mutex-protected ARC with the supplied data.
fn mutex_arc<T: send>(+user_data: T) -> mutex_arc<T> {
    let data = mutex_arc_inner {
        lock: mutex(), failed: false, data: user_data
    };
    mutex_arc { x: unsafe { shared_mutable_state(data) } }
}

impl<T: send> &mutex_arc<T> {
    /// Duplicate a mutex-protected ARC, as arc::clone.
    fn clone() -> mutex_arc<T> {
        // NB: Cloning the underlying mutex is not necessary. Its reference
        // count would be exactly the same as the shared state's.
        mutex_arc { x: unsafe { clone_shared_mutable_state(&self.x) } }
    }

    /**
     * Access the underlying mutable data with mutual exclusion from other
     * tasks. The argument closure will be run with the mutex locked; all
     * other tasks wishing to access the data will block until the closure
     * finishes running.
     *
     * The reason this function is 'unsafe' is because it is possible to
     * construct a circular reference among multiple ARCs by mutating the
     * underlying data. This creates potential for deadlock, but worse, this
     * will guarantee a memory leak of all involved ARCs. Using mutex ARCs
     * inside of other ARCs is safe in absence of circular references.
     *
     * If you wish to nest mutex_arcs, one strategy for ensuring safety at
     * runtime is to add a "nesting level counter" inside the stored data, and
     * when traversing the arcs, assert that they monotonically decrease.
     *
     * # Failure
     *
     * Failing while inside the ARC will unlock the ARC while unwinding, so
     * that other tasks won't block forever. It will also poison the ARC:
     * any tasks that subsequently try to access it (including those already
     * blocked on the mutex) will also fail immediately.
     */
    #[inline(always)]
    unsafe fn access<U>(blk: fn(x: &mut T) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        // Borrowck would complain about this if the function were not already
        // unsafe. See borrow_rwlock, far below.
        do (&state.lock).lock {
            check_poison(true, state.failed);
            state.failed = true;
            let result = blk(&mut state.data);
            state.failed = false;
            result
        }
    }
/* FIXME(#3145): Make this compile; borrowck doesn't like it..?
    /// As access(), but with a condvar, as sync::mutex.lock_cond().
    #[inline(always)]
    unsafe fn access_cond<U>(blk: fn(x: &mut T, condvar) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do (&state.lock).lock_cond |cond| {
            check_poison(true, state.failed);
            state.failed = true;
            let result = blk(&mut state.data, cond);
            state.failed = false;
            result
        }
    }
*/
}

// Common code for {mutex.access,rwlock.write}{,_cond}.
#[inline(always)]
fn check_poison(is_mutex: bool, failed: bool) {
    if failed {
        if is_mutex {
            fail ~"Poisoned mutex_arc - another task failed inside!";
        } else {
            fail ~"Poisoned rw_arc - another task failed inside!";
        }
    }
}

/****************************************************************************
 * R/W lock protected ARC
 ****************************************************************************/

struct rw_arc_inner<T: const send> { lock: rwlock; failed: bool; data: T; }
/**
 * A dual-mode ARC protected by a reader-writer lock. The data can be accessed
 * mutably or immutably, and immutably-accessing tasks may run concurrently.
 *
 * Unlike mutex_arcs, rw_arcs are safe, because they cannot be nested.
 */
struct rw_arc<T: const send> {
    x: shared_mutable_state<rw_arc_inner<T>>;
    mut cant_nest: ();
}

/// Create a reader/writer ARC with the supplied data.
fn rw_arc<T: const send>(+user_data: T) -> rw_arc<T> {
    let data = rw_arc_inner {
        lock: rwlock(), failed: false, data: user_data
    };
    rw_arc { x: unsafe { shared_mutable_state(data) }, cant_nest: () }
}

impl<T: const send> &rw_arc<T> {
    /// Duplicate a rwlock-protected ARC, as arc::clone.
    fn clone() -> rw_arc<T> {
        rw_arc { x: unsafe { clone_shared_mutable_state(&self.x) },
                 cant_nest: () }
    }

    /**
     * Access the underlying data mutably. Locks the rwlock in write mode;
     * other readers and writers will block.
     *
     * # Failure
     *
     * Failing while inside the ARC will unlock the ARC while unwinding, so
     * that other tasks won't block forever. As mutex_arc.access, it will also
     * poison the ARC, so subsequent readers and writers will both also fail.
     */
    #[inline(always)]
    fn write<U>(blk: fn(x: &mut T) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do borrow_rwlock(state).write {
            check_poison(false, state.failed);
            state.failed = true;
            let result = blk(&mut state.data);
            state.failed = false;
            result
        }
    }
/* FIXME(#3145): Make this compile; borrowck doesn't like it..?
    /// As write(), but with a condvar, as sync::rwlock.write_cond().
    #[inline(always)]
    fn write_cond<U>(blk: fn(x: &mut T, condvar) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do borrow_rwlock(state).write_cond |cond| {
            check_poison(false, state.failed);
            state.failed = true;
            let result = blk(&mut state.data, cond);
            state.failed = false;
            result
        }
    }
*/
    /**
     * Access the underlying data immutably. May run concurrently with other
     * reading tasks.
     *
     * # Failure
     *
     * Failing will unlock the ARC while unwinding. However, unlike all other
     * access modes, this will not poison the ARC.
     */
    fn read<U>(blk: fn(x: &T) -> U) -> U {
        let state = unsafe { get_shared_immutable_state(&self.x) };
        do (&state.lock).read {
            check_poison(false, state.failed);
            blk(&state.data)
        }
    }
}

// Borrowck rightly complains about immutably aliasing the rwlock in order to
// lock it. This wraps the unsafety, with the justification that the 'lock'
// field is never overwritten; only 'failed' and 'data'.
fn borrow_rwlock<T: const send>(state: &mut rw_arc_inner<T>) -> &rwlock {
    unsafe { unsafe::reinterpret_cast(&state.lock) }
}

/****************************************************************************
 * Tests
 ****************************************************************************/

#[cfg(test)]
mod tests {
    import comm::*;

    #[test]
    fn manually_share_arc() {
        let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let arc_v = arc::arc(v);

        let p = port();
        let c = chan(p);

        do task::spawn() {
            let p = port();
            c.send(chan(p));

            let arc_v = p.recv();

            let v = *arc::get::<~[int]>(&arc_v);
            assert v[3] == 4;
        };

        let c = p.recv();
        c.send(arc::clone(&arc_v));

        assert (*arc::get(&arc_v))[2] == 3;

        log(info, arc_v);
    }

    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_mutex_arc_poison() {
        let arc = ~mutex_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.access |one| {
                assert *one == 2;
            }
        };
        do arc.access |one| {
            assert *one == 1;
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_rw_arc_poison_wr() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.write |one| {
                assert *one == 2;
            }
        };
        do arc.read |one| {
            assert *one == 1;
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_rw_arc_poison_ww() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.write |one| {
                assert *one == 2;
            }
        };
        do arc.write |one| {
            assert *one == 1;
        }
    }
    #[test] #[ignore(cfg(windows))]
    fn test_rw_arc_no_poison_rr() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.read |one| {
                assert *one == 2;
            }
        };
        do arc.read |one| {
            assert *one == 1;
        }
    }
    #[test] #[ignore(cfg(windows))]
    fn test_rw_arc_no_poison_rw() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.read |one| {
                assert *one == 2;
            }
        };
        do arc.write |one| {
            assert *one == 1;
        }
    }

    #[test]
    fn test_rw_arc() {
        let arc = ~rw_arc(0);
        let arc2 = ~arc.clone();
        let (c,p) = pipes::stream();

        do task::spawn {
            do arc2.write |num| {
                for 10.times {
                    let tmp = *num;
                    *num = -1;
                    task::yield();
                    *num = tmp + 1;
                }
                c.send(());
            }
        }
        // Readers try to catch the writer in the act
        let mut children = ~[];
        for 5.times {
            let arc3 = ~arc.clone();
            do task::task().future_result(|+r| vec::push(children, r)).spawn {
                do arc3.read |num| {
                    assert *num >= 0;
                }
            }
        }
        // Wait for children to pass their asserts
        for vec::each(children) |r| { future::get(r); }
        // Wait for writer to finish
        p.recv();
        do arc.read |num| { assert *num == 10; }
    }
}
<commit_msg>Enable condvars on ARCs<commit_after>/**
 * Concurrency-enabled mechanisms for sharing mutable and/or immutable state
 * between tasks.
 */

import unsafe::{shared_mutable_state, clone_shared_mutable_state,
                get_shared_mutable_state, get_shared_immutable_state};
import sync;
import sync::{mutex, rwlock};

export arc, clone, get;
export condvar, mutex_arc, rw_arc;

/// As sync::condvar, a mechanism for unlock-and-descheduling and signalling.
struct condvar { is_mutex: bool; failed: &mut bool; cond: &sync::condvar; }

impl &condvar {
    /// Atomically exit the associated ARC and block until a signal is sent.
    fn wait() {
        assert !*self.failed;
        self.cond.wait();
        // This is why we need to wrap sync::condvar.
        check_poison(self.is_mutex, *self.failed);
    }
    /// Wake up a blocked task. Returns false if there was no blocked task.
    fn signal() -> bool {
        assert !*self.failed;
        self.cond.signal()
    }
    /// Wake up all blocked tasks. Returns the number of tasks woken.
    fn broadcast() -> uint {
        assert !*self.failed;
        self.cond.broadcast()
    }
}

/****************************************************************************
 * Immutable ARC
 ****************************************************************************/

/// An atomically reference counted wrapper for shared immutable state.
struct arc<T: const send> { x: shared_mutable_state<T>; }

/// Create an atomically reference counted wrapper.
fn arc<T: const send>(+data: T) -> arc<T> {
    arc { x: unsafe { shared_mutable_state(data) } }
}

/**
 * Access the underlying data in an atomically reference counted
 * wrapper.
 */
fn get<T: const send>(rc: &arc<T>) -> &T {
    unsafe { get_shared_immutable_state(&rc.x) }
}

/**
 * Duplicate an atomically reference counted wrapper.
 *
 * The resulting two `arc` objects will point to the same underlying data
 * object. However, one of the `arc` objects can be sent to another task,
 * allowing them to share the underlying data.
 */
fn clone<T: const send>(rc: &arc<T>) -> arc<T> {
    arc { x: unsafe { clone_shared_mutable_state(&rc.x) } }
}

/****************************************************************************
 * Mutex protected ARC (unsafe)
 ****************************************************************************/

struct mutex_arc_inner<T: send> { lock: mutex; failed: bool; data: T; }
/// An ARC with mutable data protected by a blocking mutex.
struct mutex_arc<T: send> { x: shared_mutable_state<mutex_arc_inner<T>>; }

/// Create a mutex-protected ARC with the supplied data.
fn mutex_arc<T: send>(+user_data: T) -> mutex_arc<T> {
    let data = mutex_arc_inner {
        lock: mutex(), failed: false, data: user_data
    };
    mutex_arc { x: unsafe { shared_mutable_state(data) } }
}

impl<T: send> &mutex_arc<T> {
    /// Duplicate a mutex-protected ARC, as arc::clone.
    fn clone() -> mutex_arc<T> {
        // NB: Cloning the underlying mutex is not necessary. Its reference
        // count would be exactly the same as the shared state's.
        mutex_arc { x: unsafe { clone_shared_mutable_state(&self.x) } }
    }

    /**
     * Access the underlying mutable data with mutual exclusion from other
     * tasks. The argument closure will be run with the mutex locked; all
     * other tasks wishing to access the data will block until the closure
     * finishes running.
     *
     * The reason this function is 'unsafe' is because it is possible to
     * construct a circular reference among multiple ARCs by mutating the
     * underlying data. This creates potential for deadlock, but worse, this
     * will guarantee a memory leak of all involved ARCs. Using mutex ARCs
     * inside of other ARCs is safe in absence of circular references.
     *
     * If you wish to nest mutex_arcs, one strategy for ensuring safety at
     * runtime is to add a "nesting level counter" inside the stored data, and
     * when traversing the arcs, assert that they monotonically decrease.
     *
     * # Failure
     *
     * Failing while inside the ARC will unlock the ARC while unwinding, so
     * that other tasks won't block forever. It will also poison the ARC:
     * any tasks that subsequently try to access it (including those already
     * blocked on the mutex) will also fail immediately.
     */
    #[inline(always)]
    unsafe fn access<U>(blk: fn(x: &mut T) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        // Borrowck would complain about this if the function were not already
        // unsafe. See borrow_rwlock, far below.
        do (&state.lock).lock {
            check_poison(true, state.failed);
            let _z = poison_on_fail(&mut state.failed);
            blk(&mut state.data)
        }
    }
    /// As access(), but with a condvar, as sync::mutex.lock_cond().
    #[inline(always)]
    unsafe fn access_cond<U>(blk: fn(x: &x/mut T, c: &c/condvar) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do (&state.lock).lock_cond |cond| {
            check_poison(true, state.failed);
            let _z = poison_on_fail(&mut state.failed);
            /*
            blk(&mut state.data,
                &condvar { is_mutex: true, failed: &mut state.failed,
                           cond: cond })
            */
            // XXX: Working around two seeming region bugs here
            let fref = unsafe { unsafe::reinterpret_cast(&mut state.failed) };
            let cvar = condvar { is_mutex: true, failed: fref, cond: cond };
            blk(&mut state.data, unsafe { unsafe::reinterpret_cast(&cvar) } )
        }
    }
}

// Common code for {mutex.access,rwlock.write}{,_cond}.
#[inline(always)]
fn check_poison(is_mutex: bool, failed: bool) {
    if failed {
        if is_mutex {
            fail ~"Poisoned mutex_arc - another task failed inside!";
        } else {
            fail ~"Poisoned rw_arc - another task failed inside!";
        }
    }
}

struct poison_on_fail {
    failed: &mut bool;
    new(failed: &mut bool) { self.failed = failed; }
    drop {
        /* assert !*self.failed; -- might be false in case of cond.wait() */
        if task::failing() { *self.failed = true; }
    }
}

/****************************************************************************
 * R/W lock protected ARC
 ****************************************************************************/

struct rw_arc_inner<T: const send> { lock: rwlock; failed: bool; data: T; }
/**
 * A dual-mode ARC protected by a reader-writer lock. The data can be accessed
 * mutably or immutably, and immutably-accessing tasks may run concurrently.
 *
 * Unlike mutex_arcs, rw_arcs are safe, because they cannot be nested.
 */
struct rw_arc<T: const send> {
    x: shared_mutable_state<rw_arc_inner<T>>;
    mut cant_nest: ();
}

/// Create a reader/writer ARC with the supplied data.
fn rw_arc<T: const send>(+user_data: T) -> rw_arc<T> {
    let data = rw_arc_inner {
        lock: rwlock(), failed: false, data: user_data
    };
    rw_arc { x: unsafe { shared_mutable_state(data) }, cant_nest: () }
}

impl<T: const send> &rw_arc<T> {
    /// Duplicate a rwlock-protected ARC, as arc::clone.
    fn clone() -> rw_arc<T> {
        rw_arc { x: unsafe { clone_shared_mutable_state(&self.x) },
                 cant_nest: () }
    }

    /**
     * Access the underlying data mutably. Locks the rwlock in write mode;
     * other readers and writers will block.
     *
     * # Failure
     *
     * Failing while inside the ARC will unlock the ARC while unwinding, so
     * that other tasks won't block forever. As mutex_arc.access, it will also
     * poison the ARC, so subsequent readers and writers will both also fail.
     */
    #[inline(always)]
    fn write<U>(blk: fn(x: &mut T) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do borrow_rwlock(state).write {
            check_poison(false, state.failed);
            let _z = poison_on_fail(&mut state.failed);
            blk(&mut state.data)
        }
    }
    /// As write(), but with a condvar, as sync::rwlock.write_cond().
    #[inline(always)]
    fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/condvar) -> U) -> U {
        let state = unsafe { get_shared_mutable_state(&self.x) };
        do borrow_rwlock(state).write_cond |cond| {
            check_poison(false, state.failed);
            let _z = poison_on_fail(&mut state.failed);
            /*
            blk(&mut state.data,
                &condvar { is_mutex: false, failed: &mut state.failed,
                           cond: cond })
            */
            // XXX: Working around two seeming region bugs here
            let fref = unsafe { unsafe::reinterpret_cast(&mut state.failed) };
            let cvar = condvar { is_mutex: false, failed: fref, cond: cond };
            blk(&mut state.data, unsafe { unsafe::reinterpret_cast(&cvar) } )
        }
    }
    /**
     * Access the underlying data immutably. May run concurrently with other
     * reading tasks.
     *
     * # Failure
     *
     * Failing will unlock the ARC while unwinding. However, unlike all other
     * access modes, this will not poison the ARC.
     */
    fn read<U>(blk: fn(x: &T) -> U) -> U {
        let state = unsafe { get_shared_immutable_state(&self.x) };
        do (&state.lock).read {
            check_poison(false, state.failed);
            blk(&state.data)
        }
    }
}

// Borrowck rightly complains about immutably aliasing the rwlock in order to
// lock it. This wraps the unsafety, with the justification that the 'lock'
// field is never overwritten; only 'failed' and 'data'.
fn borrow_rwlock<T: const send>(state: &mut rw_arc_inner<T>) -> &rwlock {
    unsafe { unsafe::reinterpret_cast(&state.lock) }
}

/****************************************************************************
 * Tests
 ****************************************************************************/

#[cfg(test)]
mod tests {
    import comm::*;

    #[test]
    fn manually_share_arc() {
        let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let arc_v = arc::arc(v);

        let p = port();
        let c = chan(p);

        do task::spawn() {
            let p = port();
            c.send(chan(p));

            let arc_v = p.recv();

            let v = *arc::get::<~[int]>(&arc_v);
            assert v[3] == 4;
        };

        let c = p.recv();
        c.send(arc::clone(&arc_v));

        assert (*arc::get(&arc_v))[2] == 3;

        log(info, arc_v);
    }

    #[test]
    fn test_mutex_arc_condvar() {
        let arc = ~mutex_arc(false);
        let arc2 = ~arc.clone();
        let (c,p) = pipes::oneshot();
        let (c,p) = (~mut some(c), ~mut some(p));
        do task::spawn {
            // wait until parent gets in
            pipes::recv_one(option::swap_unwrap(p));
            do arc2.access_cond |state, cond| {
                *state = true;
                cond.signal();
            }
        }
        do arc.access_cond |state, cond| {
            pipes::send_one(option::swap_unwrap(c), ());
            assert !*state;
            while !*state {
                cond.wait();
            }
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_arc_condvar_poison() {
        let arc = ~mutex_arc(1);
        let arc2 = ~arc.clone();
        let (c,p) = pipes::stream();

        do task::spawn_unlinked {
            let _ = p.recv();
            do arc2.access_cond |one, cond| {
                cond.signal();
                assert *one == 0; // Parent should fail when it wakes up.
            }
        }

        do arc.access_cond |one, cond| {
            c.send(());
            while *one == 1 {
                cond.wait();
            }
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_mutex_arc_poison() {
        let arc = ~mutex_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.access |one| {
                assert *one == 2;
            }
        };
        do arc.access |one| {
            assert *one == 1;
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_rw_arc_poison_wr() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.write |one| {
                assert *one == 2;
            }
        };
        do arc.read |one| {
            assert *one == 1;
        }
    }
    #[test] #[should_fail] #[ignore(cfg(windows))]
    fn test_rw_arc_poison_ww() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.write |one| {
                assert *one == 2;
            }
        };
        do arc.write |one| {
            assert *one == 1;
        }
    }
    #[test] #[ignore(cfg(windows))]
    fn test_rw_arc_no_poison_rr() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.read |one| {
                assert *one == 2;
            }
        };
        do arc.read |one| {
            assert *one == 1;
        }
    }
    #[test] #[ignore(cfg(windows))]
    fn test_rw_arc_no_poison_rw() {
        let arc = ~rw_arc(1);
        let arc2 = ~arc.clone();
        do task::try {
            do arc2.read |one| {
                assert *one == 2;
            }
        };
        do arc.write |one| {
            assert *one == 1;
        }
    }

    #[test]
    fn test_rw_arc() {
        let arc = ~rw_arc(0);
        let arc2 = ~arc.clone();
        let (c,p) = pipes::stream();

        do task::spawn {
            do arc2.write |num| {
                for 10.times {
                    let tmp = *num;
                    *num = -1;
                    task::yield();
                    *num = tmp + 1;
                }
                c.send(());
            }
        }
        // Readers try to catch the writer in the act
        let mut children = ~[];
        for 5.times {
            let arc3 = ~arc.clone();
            do task::task().future_result(|+r| vec::push(children, r)).spawn {
                do arc3.read |num| {
                    assert *num >= 0;
                }
            }
        }
        // Wait for children to pass their asserts
        for vec::each(children) |r| { future::get(r); }
        // Wait for writer to finish
        p.recv();
        do arc.read |num| { assert *num == 10; }
    }
}
<|endoftext|>


<commit_before>/*
Module: map

A hashmap
*/

/* Section: Types */

/*
Type: hashfn

A function that returns a hash of a value.
The hash should concentrate entropy in the
lower bits.
*/
type hashfn<K> = fn(K) -> uint;

/*
Type: eqfn

Equality
*/
type eqfn<K> = fn(K, K) -> bool;

/*
Type: hashset

A convenience type to treat a hashmap as a set
*/
type hashset<K> = hashmap<K, ()>;

/*
Obj: hashmap
*/
type hashmap<K, V> = obj {
    /*
    Method: size

    Return the number of elements in the map
    */
    fn size() -> uint;
    /*
    Method: insert

    Add a value to the map. If the map already contains a value for
    the specified key then the original value is replaced.

    Returns:

    True if the key did not already exist in the map
    */
    fn insert(K, V) -> bool;
    /*
    Method: contains_key

    Returns true if the map contains a value for the specified key
    */
    fn contains_key(K) -> bool;
    /*
    Method: get

    Get the value for the specified key

    Failure:

    If the key does not exist in the map
    */
    fn get(K) -> V;
    /*
    Method: find

    Get the value for the specified key. If the key does not exist
    in the map then returns none.
    */
    fn find(K) -> core::option::t<V>;
    /*
    Method: remove

    Remove and return a value from the map. If the key does not exist
    in the map then returns none.
    */
    fn remove(K) -> core::option::t<V>;
    /*
    Method: rehash

    Force map growth and rehashing
    */
    fn rehash();
    /*
    Method: items

    Iterate over all the key/value pairs in the map
    */
    fn items(block(K, V));
    /*
    Method: keys

    Iterate over all the keys in the map
    */
    fn keys(block(K));
    /*
    Iterate over all the values in the map
    */
    fn values(block(V));
};

/* Section: Operations */

mod chained {
    type entry<copy K, copy V> = {
        hash: uint,
        key: K,
        mutable value: V,
        mutable next: chain<K, V>
    };

    tag chain<copy K, copy V> {
        present(@entry<K, V>);
        absent;
    }

    type t<copy K, copy V> = {
        mutable size: uint,
        mutable chains: [mutable chain<K,V>],
        hasher: hashfn<K>,
        eqer: eqfn<K>
    };

    tag search_result<copy K, copy V> {
        not_found;
        found_first(uint, @entry<K,V>);
        found_after(@entry<K,V>, @entry<K,V>);
    }

    fn search_rem<copy K, copy V>(tbl: t<K,V>,
                                  k: K,
                                  h: uint,
                                  idx: uint,
                                  e_root: @entry<K,V>) -> search_result<K,V> {
        let e0 = e_root;
        let comp = 1u;   // for logging
        while true {
            alt e0.next {
              absent. {
                log("search_tbl", "absent", "comparisons", comp,
                    "hash", h, "idx", idx);

                ret not_found;
              }
              present(e1) {
                comp += 1u;
                let e1_key = e1.key; // Satisfy alias checker.
                if e1.hash == h && tbl.eqer(e1_key, k) {
                    log("search_tbl", "present", "comparisons", comp,
                        "hash", h, "idx", idx);
                    ret found_after(e0, e1);
                } else {
                    e0 = e1;
                }
              }
            }
        }
        util::unreachable();
    }

    fn search_tbl<copy K, copy V>(
        tbl: t<K,V>, k: K, h: uint) -> search_result<K,V> {
        let idx = h % vec::len(tbl.chains);
        alt tbl.chains[idx] {
          absent. {
            log("search_tbl", "absent", "comparisons", 0u,
                "hash", h, "idx", idx);
            ret not_found;
          }
          present(e) {
            let e_key = e.key; // Satisfy alias checker.
            if e.hash == h && tbl.eqer(e_key, k) {
                log("search_tbl", "present", "comparisons", 1u,
                    "hash", h, "idx", idx);
                ret found_first(idx, e);
            } else {
                ret search_rem(tbl, k, h, idx, e);
            }
          }
        }
    }

    fn insert<copy K, copy V>(tbl: t<K,V>, k: K, v: V) -> bool {
        let hash = tbl.hasher(k);
        alt search_tbl(tbl, k, hash) {
          not_found. {
            tbl.size += 1u;
            let idx = hash % vec::len(tbl.chains);
            let old_chain = tbl.chains[idx];
            tbl.chains[idx] = present(@{
                hash: hash,
                key: k,
                mutable value: v,
                mutable next: old_chain});
            ret true;
          }
          found_first(_, entry) {
            entry.value = v;
            ret false;
          }
          found_after(_, entry) {
            entry.value = v;
            ret false
          }
        }
    }

    fn get<copy K, copy V>(tbl: t<K,V>, k: K) -> core::option::t<V> {
        alt search_tbl(tbl, k, tbl.hasher(k)) {
          not_found. {
            ret core::option::none;
          }

          found_first(_, entry) {
            ret core::option::some(entry.value);
          }

          found_after(_, entry) {
            ret core::option::some(entry.value);
          }
        }
    }

    fn remove<copy K, copy V>(tbl: t<K,V>, k: K) -> core::option::t<V> {
        alt search_tbl(tbl, k, tbl.hasher(k)) {
          not_found. {
            ret core::option::none;
          }

          found_first(idx, entry) {
            tbl.size -= 1u;
            tbl.chains[idx] = entry.next;
            ret core::option::some(entry.value);
          }

          found_after(eprev, entry) {
            tbl.size -= 1u;
            eprev.next = entry.next;
            ret core::option::some(entry.value);
          }
        }
    }

    fn chains<copy K, copy V>(nchains: uint) -> [mutable chain<K,V>] {
        ret vec::init_elt_mut(absent, nchains);
    }

    fn foreach_entry<copy K, copy V>(chain0: chain<K,V>,
                                     blk: block(@entry<K,V>)) {
        let chain = chain0;
        while true {
            alt chain {
              absent. { ret; }
              present(entry) {
                let next = entry.next;
                blk(entry); // may modify entry.next!
                chain = next;
              }
            }
        }
    }

    fn foreach_chain<copy K, copy V>(chains: [const chain<K,V>],
                                     blk: block(@entry<K,V>)) {
        let i = 0u, n = vec::len(chains);
        while i < n {
            foreach_entry(chains[i], blk);
            i += 1u;
        }
    }

    fn rehash<copy K, copy V>(tbl: t<K,V>) {
        let old_chains = tbl.chains;
        let n_old_chains = vec::len(old_chains);
        let n_new_chains: uint = uint::next_power_of_two(n_old_chains + 1u);
        tbl.chains = chains(n_new_chains);
        foreach_chain(old_chains) { |entry|
            let idx = entry.hash % n_new_chains;
            entry.next = tbl.chains[idx];
            tbl.chains[idx] = present(entry);
        }
    }

    fn items<copy K, copy V>(tbl: t<K,V>, blk: block(K,V)) {
        let tbl_chains = tbl.chains;  // Satisfy alias checker.
        foreach_chain(tbl_chains) { |entry|
            let key = entry.key;
            let value = entry.value;
            blk(key, value);
        }
    }

    obj o<copy K, copy V>(tbl: @t<K,V>,
                          lf: util::rational) {
        fn size() -> uint {
            ret tbl.size;
        }

        fn insert(k: K, v: V) -> bool {
            let nchains = vec::len(tbl.chains);
            let load = {num:tbl.size + 1u as int, den:nchains as int};
            if !util::rational_leq(load, lf) {
                rehash(*tbl);
            }
            ret insert(*tbl, k, v);
        }

        fn contains_key(k: K) -> bool {
            ret core::option::is_some(get(*tbl, k));
        }

        fn get(k: K) -> V {
            ret core::option::get(get(*tbl, k));
        }

        fn find(k: K) -> core::option::t<V> {
            ret get(*tbl, k);
        }

        fn remove(k: K) -> core::option::t<V> {
            ret remove(*tbl, k);
        }

        fn rehash() {
            rehash(*tbl);
        }

        fn items(blk: block(K, V)) {
            items(*tbl, blk);
        }

        fn keys(blk: block(K)) {
            items(*tbl) { |k, _v| blk(k) }
        }

        fn values(blk: block(V)) {
            items(*tbl) { |_k, v| blk(v) }
        }
    }

    fn mk<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>) -> hashmap<K,V> {
        let initial_capacity: uint = 32u; // 2^5
        let t = @{mutable size: 0u,
                  mutable chains: chains(initial_capacity),
                  hasher: hasher,
                  eqer: eqer};
        ret o(t, {num:3, den:4});
    }
}

/*
Function: mk_hashmap

Construct a hashmap.

Parameters:

hasher - The hash function for key type K
eqer - The equality function for key type K
*/
fn mk_hashmap<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>)
    -> hashmap<K, V> {
    ret chained::mk(hasher, eqer);
}

/*
Function: new_str_hash

Construct a hashmap for string keys
*/
fn new_str_hash<copy V>() -> hashmap<str, V> {
    ret mk_hashmap(str::hash, str::eq);
}

/*
Function: new_int_hash

Construct a hashmap for int keys
*/
fn new_int_hash<copy V>() -> hashmap<int, V> {
    fn hash_int(&&x: int) -> uint { ret x as uint; }
    fn eq_int(&&a: int, &&b: int) -> bool { ret a == b; }
    ret mk_hashmap(hash_int, eq_int);
}

/*
Function: new_uint_hash

Construct a hashmap for uint keys
*/
fn new_uint_hash<copy V>() -> hashmap<uint, V> {
    fn hash_uint(&&x: uint) -> uint { ret x; }
    fn eq_uint(&&a: uint, &&b: uint) -> bool { ret a == b; }
    ret mk_hashmap(hash_uint, eq_uint);
}

/*
Function: set_add

Convenience function for adding keys to a hashmap with nil type keys
*/
fn set_add<K>(set: hashset<K>, key: K) -> bool { ret set.insert(key, ()); }

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Whitespace cleanup.<commit_after>/*
Module: map

A hashmap
*/

/* Section: Types */

/*
Type: hashfn

A function that returns a hash of a value.
The hash should concentrate entropy in the
lower bits.
*/
type hashfn<K> = fn(K) -> uint;

/*
Type: eqfn

Equality
*/
type eqfn<K> = fn(K, K) -> bool;

/*
Type: hashset

A convenience type to treat a hashmap as a set
*/
type hashset<K> = hashmap<K, ()>;

/*
Obj: hashmap
*/
type hashmap<K, V> = obj {
    /*
    Method: size

    Return the number of elements in the map
    */
    fn size() -> uint;
    /*
    Method: insert

    Add a value to the map. If the map already contains a value for
    the specified key then the original value is replaced.

    Returns:

    True if the key did not already exist in the map
    */
    fn insert(K, V) -> bool;
    /*
    Method: contains_key

    Returns true if the map contains a value for the specified key
    */
    fn contains_key(K) -> bool;
    /*
    Method: get

    Get the value for the specified key

    Failure:

    If the key does not exist in the map
    */
    fn get(K) -> V;
    /*
    Method: find

    Get the value for the specified key. If the key does not exist
    in the map then returns none.
    */
    fn find(K) -> core::option::t<V>;
    /*
    Method: remove

    Remove and return a value from the map. If the key does not exist
    in the map then returns none.
    */
    fn remove(K) -> core::option::t<V>;
    /*
    Method: rehash

    Force map growth and rehashing
    */
    fn rehash();
    /*
    Method: items

    Iterate over all the key/value pairs in the map
    */
    fn items(block(K, V));
    /*
    Method: keys

    Iterate over all the keys in the map
    */
    fn keys(block(K));

    /*
    Iterate over all the values in the map
    */
    fn values(block(V));
};

/* Section: Operations */

mod chained {
    type entry<copy K, copy V> = {
        hash: uint,
        key: K,
        mutable value: V,
        mutable next: chain<K, V>
    };

    tag chain<copy K, copy V> {
        present(@entry<K, V>);
        absent;
    }

    type t<copy K, copy V> = {
        mutable size: uint,
        mutable chains: [mutable chain<K,V>],
        hasher: hashfn<K>,
        eqer: eqfn<K>
    };

    tag search_result<copy K, copy V> {
        not_found;
        found_first(uint, @entry<K,V>);
        found_after(@entry<K,V>, @entry<K,V>);
    }

    fn search_rem<copy K, copy V>(tbl: t<K,V>,
                                  k: K,
                                  h: uint,
                                  idx: uint,
                                  e_root: @entry<K,V>) -> search_result<K,V> {
        let e0 = e_root;
        let comp = 1u;   // for logging
        while true {
            alt e0.next {
              absent. {
                log("search_tbl", "absent", "comparisons", comp,
                    "hash", h, "idx", idx);

                ret not_found;
              }
              present(e1) {
                comp += 1u;
                let e1_key = e1.key; // Satisfy alias checker.
                if e1.hash == h && tbl.eqer(e1_key, k) {
                    log("search_tbl", "present", "comparisons", comp,
                        "hash", h, "idx", idx);
                    ret found_after(e0, e1);
                } else {
                    e0 = e1;
                }
              }
            }
        }
        util::unreachable();
    }

    fn search_tbl<copy K, copy V>(
        tbl: t<K,V>, k: K, h: uint) -> search_result<K,V> {
        let idx = h % vec::len(tbl.chains);
        alt tbl.chains[idx] {
          absent. {
            log("search_tbl", "absent", "comparisons", 0u,
                "hash", h, "idx", idx);
            ret not_found;
          }
          present(e) {
            let e_key = e.key; // Satisfy alias checker.
            if e.hash == h && tbl.eqer(e_key, k) {
                log("search_tbl", "present", "comparisons", 1u,
                    "hash", h, "idx", idx);
                ret found_first(idx, e);
            } else {
                ret search_rem(tbl, k, h, idx, e);
            }
          }
        }
    }

    fn insert<copy K, copy V>(tbl: t<K,V>, k: K, v: V) -> bool {
        let hash = tbl.hasher(k);
        alt search_tbl(tbl, k, hash) {
          not_found. {
            tbl.size += 1u;
            let idx = hash % vec::len(tbl.chains);
            let old_chain = tbl.chains[idx];
            tbl.chains[idx] = present(@{
                hash: hash,
                key: k,
                mutable value: v,
                mutable next: old_chain});
            ret true;
          }
          found_first(_, entry) {
            entry.value = v;
            ret false;
          }
          found_after(_, entry) {
            entry.value = v;
            ret false
          }
        }
    }

    fn get<copy K, copy V>(tbl: t<K,V>, k: K) -> core::option::t<V> {
        alt search_tbl(tbl, k, tbl.hasher(k)) {
          not_found. {
            ret core::option::none;
          }

          found_first(_, entry) {
            ret core::option::some(entry.value);
          }

          found_after(_, entry) {
            ret core::option::some(entry.value);
          }
        }
    }

    fn remove<copy K, copy V>(tbl: t<K,V>, k: K) -> core::option::t<V> {
        alt search_tbl(tbl, k, tbl.hasher(k)) {
          not_found. {
            ret core::option::none;
          }

          found_first(idx, entry) {
            tbl.size -= 1u;
            tbl.chains[idx] = entry.next;
            ret core::option::some(entry.value);
          }

          found_after(eprev, entry) {
            tbl.size -= 1u;
            eprev.next = entry.next;
            ret core::option::some(entry.value);
          }
        }
    }

    fn chains<copy K, copy V>(nchains: uint) -> [mutable chain<K,V>] {
        ret vec::init_elt_mut(absent, nchains);
    }

    fn foreach_entry<copy K, copy V>(chain0: chain<K,V>,
                                     blk: block(@entry<K,V>)) {
        let chain = chain0;
        while true {
            alt chain {
              absent. { ret; }
              present(entry) {
                let next = entry.next;
                blk(entry); // may modify entry.next!
                chain = next;
              }
            }
        }
    }

    fn foreach_chain<copy K, copy V>(chains: [const chain<K,V>],
                                     blk: block(@entry<K,V>)) {
        let i = 0u, n = vec::len(chains);
        while i < n {
            foreach_entry(chains[i], blk);
            i += 1u;
        }
    }

    fn rehash<copy K, copy V>(tbl: t<K,V>) {
        let old_chains = tbl.chains;
        let n_old_chains = vec::len(old_chains);
        let n_new_chains: uint = uint::next_power_of_two(n_old_chains + 1u);
        tbl.chains = chains(n_new_chains);
        foreach_chain(old_chains) { |entry|
            let idx = entry.hash % n_new_chains;
            entry.next = tbl.chains[idx];
            tbl.chains[idx] = present(entry);
        }
    }

    fn items<copy K, copy V>(tbl: t<K,V>, blk: block(K,V)) {
        let tbl_chains = tbl.chains;  // Satisfy alias checker.
        foreach_chain(tbl_chains) { |entry|
            let key = entry.key;
            let value = entry.value;
            blk(key, value);
        }
    }

    obj o<copy K, copy V>(tbl: @t<K,V>,
                          lf: util::rational) {
        fn size() -> uint {
            ret tbl.size;
        }

        fn insert(k: K, v: V) -> bool {
            let nchains = vec::len(tbl.chains);
            let load = {num:tbl.size + 1u as int, den:nchains as int};
            if !util::rational_leq(load, lf) {
                rehash(*tbl);
            }
            ret insert(*tbl, k, v);
        }

        fn contains_key(k: K) -> bool {
            ret core::option::is_some(get(*tbl, k));
        }

        fn get(k: K) -> V {
            ret core::option::get(get(*tbl, k));
        }

        fn find(k: K) -> core::option::t<V> {
            ret get(*tbl, k);
        }

        fn remove(k: K) -> core::option::t<V> {
            ret remove(*tbl, k);
        }

        fn rehash() {
            rehash(*tbl);
        }

        fn items(blk: block(K, V)) {
            items(*tbl, blk);
        }

        fn keys(blk: block(K)) {
            items(*tbl) { |k, _v| blk(k) }
        }

        fn values(blk: block(V)) {
            items(*tbl) { |_k, v| blk(v) }
        }
    }

    fn mk<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>) -> hashmap<K,V> {
        let initial_capacity: uint = 32u; // 2^5
        let t = @{mutable size: 0u,
                  mutable chains: chains(initial_capacity),
                  hasher: hasher,
                  eqer: eqer};
        ret o(t, {num:3, den:4});
    }
}

/*
Function: mk_hashmap

Construct a hashmap.

Parameters:

hasher - The hash function for key type K
eqer - The equality function for key type K
*/
fn mk_hashmap<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>)
    -> hashmap<K, V> {
    ret chained::mk(hasher, eqer);
}

/*
Function: new_str_hash

Construct a hashmap for string keys
*/
fn new_str_hash<copy V>() -> hashmap<str, V> {
    ret mk_hashmap(str::hash, str::eq);
}

/*
Function: new_int_hash

Construct a hashmap for int keys
*/
fn new_int_hash<copy V>() -> hashmap<int, V> {
    fn hash_int(&&x: int) -> uint { ret x as uint; }
    fn eq_int(&&a: int, &&b: int) -> bool { ret a == b; }
    ret mk_hashmap(hash_int, eq_int);
}

/*
Function: new_uint_hash

Construct a hashmap for uint keys
*/
fn new_uint_hash<copy V>() -> hashmap<uint, V> {
    fn hash_uint(&&x: uint) -> uint { ret x; }
    fn eq_uint(&&a: uint, &&b: uint) -> bool { ret a == b; }
    ret mk_hashmap(hash_uint, eq_uint);
}

/*
Function: set_add

Convenience function for adding keys to a hashmap with nil type keys
*/
fn set_add<K>(set: hashset<K>, key: K) -> bool { ret set.insert(key, ()); }

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>/*
 * CPU   Main Memory (2)
 *  |         |
 *  ------------------------
 *       |                 |
 *   Registers (1)        ROM (3)
 *
 *
 */
<commit_msg>basic memory implementation<commit_after>/*
 * CPU   Main Memory (2)
 *  |         |
 *  ------------------------
 *       |                 |
 *   Registers (1)        ROM (3)
 *
 *
 */

const MEMORY_SIZE: u16 = 0xffff;

pub struct Memory(Vec<u8>);

impl Memory {

    /// Init a new memory unit
    pub fn new() -> Memory {
        let mut mem: Vec<u8> = Vec::with_capacity(MEMORY_SIZE as usize);
        for _ in 0..MEMORY_SIZE {
            mem.push(0);
        }
        Memory(mem)
    }

    /// Read a byte from the memory
    pub fn read_byte(&self, addr: u16) -> u8 {
        self.0[addr as usize]
    }

    /// Read a range of bytes from the memory
    pub fn read_range(&self, addr: (u16, u16)) -> Vec<u8> {
        let mut a: Vec<u8> = Vec::with_capacity((addr.1 - addr.0) as usize);
        for x in addr.0..addr.1 {
            a.push(self.0[x as usize])
        }
        a
    }

}
<|endoftext|>


<commit_before>// Rx -- Reactive programming for Rust
// 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.

use observer::Observer;
use observer::{NextObserver, CompletedObserver, ErrorObserver, OptionObserver, ResultObserver};
use std::fmt::Debug;
use transform::{ContinueWithObservable, MapErrorObservable, MapObservable};

/// A stream of values.
///
/// An observable represents a stream of values, much like an iterator,
/// but instead of being “pull-based” like an iterator, it is “push-based”.
/// Multiple observers can subscribe to an observable and when the observable
/// produces a value, all observers get called with this value.
///
/// An observable can be _finite_ or _infinite_. An example of an infinite
/// observable are mouse clicks: you never know if the user is going to click
/// once more. An example of a finite observable are the results of a database
/// query: a database is can hold only finitely many records, so one result is
/// the last one.
///
/// A finite observable can end in two ways:
///
///  * **Completed**: when the observable ends normally.
///    For instance, an observable of database query results
///    will complete after the last result has been produced.
///  * **Failed**: when an error occurred.
///    For instance, an observable of database query results
///    may fail if the connection is lost.
///
/// Failures are fatal: after an observable produces an error, it will not
/// produce any new values. If this is not the desired behavior, you can
/// use an observable of `Result`.
pub trait Observable {
    /// The value produced by the observable.
    type Item: Clone;

    /// The error produced if the observable fails.
    type Error: Clone;

    /// The result of subscribing an observer.
    type Subscription: Drop;

    /// Subscribes an observer and returns the subscription.
    ///
    /// After subscription, `on_next` will be called on the observer for every
    /// value produced. If the observable completes, `on_completed` is called.
    /// If the observable fails with an error, `on_error` is called. It is
    /// guaranteed that no methods will be called on the observer after
    /// `on_completed` or `on_error` have been called.
    ///
    /// _When_ the observer is called is not part of the observable contract,
    /// it depends on the kind of observable. The observer may be called before
    /// `subscribe` returns, or it may be called in the future.
    ///
    /// The returned value represents the subscription. Dropping the subscription
    /// will prevent further calls on the observer.
    fn subscribe<O>(&mut self, observer: O) -> Self::Subscription
        where O: Observer<Self::Item, Self::Error>;

    /// Subscribes a function to handle values produced by the observable.
    ///
    /// For every value produced by the observable, `on_next` is called.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_next<FnNext>(&mut self,
                              on_next: FnNext)
                              -> Self::Subscription
        where Self::Error: Debug, FnNext: FnMut(Self::Item) {
        let observer = NextObserver {
            fn_next: on_next,
        };
        self.subscribe(observer)
    }

    /// Subscribes functions to handle next and completion.
    ///
    /// For every value produced by the observable, `on_next` is called. If the
    /// observable completes, `on_completed` is called. A failure will cause a
    /// panic. After `on_completed` has been called, it is guaranteed that neither
    /// `on_next` nor `on_completed` is called again.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_completed<FnNext, FnCompleted>(&mut self,
                                                on_next: FnNext,
                                                on_completed: FnCompleted)
                                                -> Self::Subscription
        where Self::Error: Debug, FnNext: FnMut(Self::Item), FnCompleted: FnOnce() {
        let observer = CompletedObserver {
            fn_next: on_next,
            fn_completed: on_completed,
        };
        self.subscribe(observer)
    }

    /// Subscribes functions to handle next, completion, and error.
    ///
    /// For every value produced by the observable, `on_next` is called. If the
    /// observable completes, `on_completed` is called. If it fails, `on_error`
    /// is called. After `on_completed` or `on_error` have been called, it is
    /// guaranteed that none of the three functions are called again.
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_error<FnNext, FnCompleted, FnError>(&mut self,
                                                     on_next: FnNext,
                                                     on_completed: FnCompleted,
                                                     on_error: FnError)
                                                     -> Self::Subscription
        where FnNext: FnMut(Self::Item), FnCompleted: FnOnce(), FnError: FnOnce(Self::Error) {
        let observer = ErrorObserver {
            fn_next: on_next,
            fn_completed: on_completed,
            fn_error: on_error,
        };
        self.subscribe(observer)
    }

    /// Subscribes a function that takes an option.
    ///
    /// The function translates into an observer as follows:
    ///
    ///  * `on_next(x)`: calls the functions with `Some(x)`.
    ///  * `on_completed()`: calls the function with `None`.
    ///  * `on_error(error)`: panics.
    ///
    /// After the function has been called with `None`,
    /// it is guaranteed never to be called again.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_option<FnOption>(&mut self,
                                  on_next_or_completed: FnOption)
                                  -> Self::Subscription
        where Self::Error: Debug, FnOption: FnMut(Option<Self::Item>) {
        let observer = OptionObserver {
            fn_option: on_next_or_completed
        };
        self.subscribe(observer)
    }

    /// Subscribes a function that takes a result of an option.
    ///
    /// The function translates into an observer as follows:
    ///
    ///  * `on_next(x)`: calls the function with `Ok(Some(x))`.
    ///  * `on_completed()`: calls the function with `Ok(None)`.
    ///  * `on_error(error)`: calls the function with `Err(error)`.
    ///
    /// After the function has been called with `Ok(None)` or `Err(error)`,
    /// it is guaranteed never to be called again.
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_result<FnResult>(&mut self,
                                  on_next_or_completed_or_error: FnResult)
                                  -> Self::Subscription
        where FnResult: FnMut(Result<Option<Self::Item>, Self::Error>) {
        let observer = ResultObserver {
            fn_result: on_next_or_completed_or_error
        };
        self.subscribe(observer)
    }

    /// Transforms an observable by applying f to every value produced.
    fn map<'s, U, F>(&'s mut self, f: F) -> MapObservable<'s, Self, F>
        where F: Fn(Self::Item) -> U {
        MapObservable::new(self, f)
    }

    /// Transforms an observable by applying f the error in case of failure.
    fn map_error<'s, F, G>(&'s mut self, f: G) -> MapErrorObservable<'s, Self, G>
        where G: Fn(Self::Error) -> F {
        MapErrorObservable::new(self, f)
    }

    /// Joins two observables sequentially.
    ///
    /// After the current observable completes, an observer will start to
    /// receive values from `next` until that observable completes or fails.
    /// The `next` observable is only subscribed to after the current observable
    /// completes.
    fn continue_with<'s, ObNext>(&'s mut self, next: &'s mut ObNext) -> ContinueWithObservable<'s, Self, ObNext>
        where ObNext: Observable<Item = Self::Item, Error = Self::Error> {
        ContinueWithObservable::new(self, next)
    }
}
<commit_msg>Add note about Drop bound on subscription<commit_after>// Rx -- Reactive programming for Rust
// 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.

use observer::Observer;
use observer::{NextObserver, CompletedObserver, ErrorObserver, OptionObserver, ResultObserver};
use std::fmt::Debug;
use transform::{ContinueWithObservable, MapErrorObservable, MapObservable};

/// A stream of values.
///
/// An observable represents a stream of values, much like an iterator,
/// but instead of being “pull-based” like an iterator, it is “push-based”.
/// Multiple observers can subscribe to an observable and when the observable
/// produces a value, all observers get called with this value.
///
/// An observable can be _finite_ or _infinite_. An example of an infinite
/// observable are mouse clicks: you never know if the user is going to click
/// once more. An example of a finite observable are the results of a database
/// query: a database is can hold only finitely many records, so one result is
/// the last one.
///
/// A finite observable can end in two ways:
///
///  * **Completed**: when the observable ends normally.
///    For instance, an observable of database query results
///    will complete after the last result has been produced.
///  * **Failed**: when an error occurred.
///    For instance, an observable of database query results
///    may fail if the connection is lost.
///
/// Failures are fatal: after an observable produces an error, it will not
/// produce any new values. If this is not the desired behavior, you can
/// use an observable of `Result`.
pub trait Observable {
    /// The value produced by the observable.
    type Item: Clone;

    /// The error produced if the observable fails.
    type Error: Clone;

    /// The result of subscribing an observer.
    // TODO: This drop bound is not required and it only complicates stuff, remove it.
    type Subscription: Drop;

    /// Subscribes an observer and returns the subscription.
    ///
    /// After subscription, `on_next` will be called on the observer for every
    /// value produced. If the observable completes, `on_completed` is called.
    /// If the observable fails with an error, `on_error` is called. It is
    /// guaranteed that no methods will be called on the observer after
    /// `on_completed` or `on_error` have been called.
    ///
    /// _When_ the observer is called is not part of the observable contract,
    /// it depends on the kind of observable. The observer may be called before
    /// `subscribe` returns, or it may be called in the future.
    ///
    /// The returned value represents the subscription. Dropping the subscription
    /// will prevent further calls on the observer.
    fn subscribe<O>(&mut self, observer: O) -> Self::Subscription
        where O: Observer<Self::Item, Self::Error>;

    /// Subscribes a function to handle values produced by the observable.
    ///
    /// For every value produced by the observable, `on_next` is called.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_next<FnNext>(&mut self,
                              on_next: FnNext)
                              -> Self::Subscription
        where Self::Error: Debug, FnNext: FnMut(Self::Item) {
        let observer = NextObserver {
            fn_next: on_next,
        };
        self.subscribe(observer)
    }

    /// Subscribes functions to handle next and completion.
    ///
    /// For every value produced by the observable, `on_next` is called. If the
    /// observable completes, `on_completed` is called. A failure will cause a
    /// panic. After `on_completed` has been called, it is guaranteed that neither
    /// `on_next` nor `on_completed` is called again.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_completed<FnNext, FnCompleted>(&mut self,
                                                on_next: FnNext,
                                                on_completed: FnCompleted)
                                                -> Self::Subscription
        where Self::Error: Debug, FnNext: FnMut(Self::Item), FnCompleted: FnOnce() {
        let observer = CompletedObserver {
            fn_next: on_next,
            fn_completed: on_completed,
        };
        self.subscribe(observer)
    }

    /// Subscribes functions to handle next, completion, and error.
    ///
    /// For every value produced by the observable, `on_next` is called. If the
    /// observable completes, `on_completed` is called. If it fails, `on_error`
    /// is called. After `on_completed` or `on_error` have been called, it is
    /// guaranteed that none of the three functions are called again.
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_error<FnNext, FnCompleted, FnError>(&mut self,
                                                     on_next: FnNext,
                                                     on_completed: FnCompleted,
                                                     on_error: FnError)
                                                     -> Self::Subscription
        where FnNext: FnMut(Self::Item), FnCompleted: FnOnce(), FnError: FnOnce(Self::Error) {
        let observer = ErrorObserver {
            fn_next: on_next,
            fn_completed: on_completed,
            fn_error: on_error,
        };
        self.subscribe(observer)
    }

    /// Subscribes a function that takes an option.
    ///
    /// The function translates into an observer as follows:
    ///
    ///  * `on_next(x)`: calls the functions with `Some(x)`.
    ///  * `on_completed()`: calls the function with `None`.
    ///  * `on_error(error)`: panics.
    ///
    /// After the function has been called with `None`,
    /// it is guaranteed never to be called again.
    ///
    /// **This subscription panics if the observable fails with an error.**
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_option<FnOption>(&mut self,
                                  on_next_or_completed: FnOption)
                                  -> Self::Subscription
        where Self::Error: Debug, FnOption: FnMut(Option<Self::Item>) {
        let observer = OptionObserver {
            fn_option: on_next_or_completed
        };
        self.subscribe(observer)
    }

    /// Subscribes a function that takes a result of an option.
    ///
    /// The function translates into an observer as follows:
    ///
    ///  * `on_next(x)`: calls the function with `Ok(Some(x))`.
    ///  * `on_completed()`: calls the function with `Ok(None)`.
    ///  * `on_error(error)`: calls the function with `Err(error)`.
    ///
    /// After the function has been called with `Ok(None)` or `Err(error)`,
    /// it is guaranteed never to be called again.
    ///
    /// See also [`subscribe()`](#tymethod.subscribe).
    fn subscribe_result<FnResult>(&mut self,
                                  on_next_or_completed_or_error: FnResult)
                                  -> Self::Subscription
        where FnResult: FnMut(Result<Option<Self::Item>, Self::Error>) {
        let observer = ResultObserver {
            fn_result: on_next_or_completed_or_error
        };
        self.subscribe(observer)
    }

    /// Transforms an observable by applying f to every value produced.
    fn map<'s, U, F>(&'s mut self, f: F) -> MapObservable<'s, Self, F>
        where F: Fn(Self::Item) -> U {
        MapObservable::new(self, f)
    }

    /// Transforms an observable by applying f the error in case of failure.
    fn map_error<'s, F, G>(&'s mut self, f: G) -> MapErrorObservable<'s, Self, G>
        where G: Fn(Self::Error) -> F {
        MapErrorObservable::new(self, f)
    }

    /// Joins two observables sequentially.
    ///
    /// After the current observable completes, an observer will start to
    /// receive values from `next` until that observable completes or fails.
    /// The `next` observable is only subscribed to after the current observable
    /// completes.
    fn continue_with<'s, ObNext>(&'s mut self, next: &'s mut ObNext) -> ContinueWithObservable<'s, Self, ObNext>
        where ObNext: Observable<Item = Self::Item, Error = Self::Error> {
        ContinueWithObservable::new(self, next)
    }
}
<|endoftext|>


<commit_before>use rustc_hash::FxHashSet;

use ra_syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        if let Some(range) =
                            contiguous_range_for_comment(comment, &mut visited_comments)
                        {
                            res.push(Fold { range, kind: FoldKind::Comment })
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        RECORD_FIELD_DEF_LIST
        | RECORD_FIELD_PAT_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK
        | ENUM_VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::from_to(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        visited.insert(c.clone());
                        last = c;
                        continue;
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::from_to(
            first.syntax().text_range().start(),
            last.syntax().text_range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

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

    fn do_check(text: &str, fold_kinds: &[FoldKind]) {
        let (ranges, text) = extract_ranges(text, "fold");
        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());

        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );
        assert_eq!(
            folds.len(),
            fold_kinds.len(),
            "The amount of fold kinds is different than the expected amount"
        );
        for ((fold, range), fold_kind) in
            folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
        {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());
            assert_eq!(&fold.kind, fold_kind);
        }
    }

    #[test]
    fn test_fold_comments() {
        let text = r#"
<fold>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold>{
    <fold>// We should
    // also
    // fold
    // this one.</fold>
    <fold>//! But this one is different
    //! because it has another flavor</fold>
    <fold>/* As does this
    multiline comment */</fold>
}</fold>"#;

        let fold_kinds = &[
            FoldKind::Comment,
            FoldKind::Block,
            FoldKind::Comment,
            FoldKind::Comment,
            FoldKind::Comment,
        ];
        do_check(text, fold_kinds);
    }

    #[test]
    fn test_fold_imports() {
        let text = r#"
<fold>use std::<fold>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_mods() {
        let text = r#"

pub mod foo;
<fold>mod after_pub;
mod after_pub_next;</fold>

<fold>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_and_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

<fold>use std::collections::<fold>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold>{
}</fold>"#;

        let folds = &[
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Block,
            FoldKind::Block,
        ];
        do_check(text, folds);
    }

    #[test]
    fn test_folds_macros() {
        let text = r#"
macro_rules! foo <fold>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#;

        let folds = &[FoldKind::Block];
        do_check(text, folds);
    }
}
<commit_msg>`fold_kind`: `MATCH_ARM_LIST => FoldKind::Block`<commit_after>use rustc_hash::FxHashSet;

use ra_syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        if let Some(range) =
                            contiguous_range_for_comment(comment, &mut visited_comments)
                        {
                            res.push(Fold { range, kind: FoldKind::Comment })
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        RECORD_FIELD_DEF_LIST
        | RECORD_FIELD_PAT_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK
        | MATCH_ARM_LIST
        | ENUM_VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::from_to(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        visited.insert(c.clone());
                        last = c;
                        continue;
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::from_to(
            first.syntax().text_range().start(),
            last.syntax().text_range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

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

    fn do_check(text: &str, fold_kinds: &[FoldKind]) {
        let (ranges, text) = extract_ranges(text, "fold");
        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());

        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );
        assert_eq!(
            folds.len(),
            fold_kinds.len(),
            "The amount of fold kinds is different than the expected amount"
        );
        for ((fold, range), fold_kind) in
            folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
        {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());
            assert_eq!(&fold.kind, fold_kind);
        }
    }

    #[test]
    fn test_fold_comments() {
        let text = r#"
<fold>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold>{
    <fold>// We should
    // also
    // fold
    // this one.</fold>
    <fold>//! But this one is different
    //! because it has another flavor</fold>
    <fold>/* As does this
    multiline comment */</fold>
}</fold>"#;

        let fold_kinds = &[
            FoldKind::Comment,
            FoldKind::Block,
            FoldKind::Comment,
            FoldKind::Comment,
            FoldKind::Comment,
        ];
        do_check(text, fold_kinds);
    }

    #[test]
    fn test_fold_imports() {
        let text = r#"
<fold>use std::<fold>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_mods() {
        let text = r#"

pub mod foo;
<fold>mod after_pub;
mod after_pub_next;</fold>

<fold>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_and_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

<fold>use std::collections::<fold>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold>{
}</fold>"#;

        let folds = &[
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Block,
            FoldKind::Block,
        ];
        do_check(text, folds);
    }

    #[test]
    fn test_folds_macros() {
        let text = r#"
macro_rules! foo <fold>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#;

        let folds = &[FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_match_arms() {
        let text = r#"
fn main() <fold>{
    match 0 <fold>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>"#;

        let folds = &[
            FoldKind::Block,
            FoldKind::Block,
        ];
        do_check(text, folds);
    }
}
<|endoftext|>


<commit_before>
pub mod ical;
pub mod vcard;

use std::io::BufRead;
use std::cell::RefCell;
use std::error::Error;
use std::fmt;

use line::parser;

pub trait Component {
    fn add_sub_component<B: BufRead>(&mut self,
                                     value: &str,
                                     line_parser: &RefCell<parser::LineParser<B>>)
                                     -> Result<(), ParseError>;
    fn add_property(&mut self, property: parser::LineParsed);

    fn parse<B: BufRead>(&mut self,
                         line_parser: &RefCell<parser::LineParser<B>>)
                         -> Result<(), ParseError> {

        loop {
            let line: parser::LineParsed;

            {
                line = match line_parser.borrow_mut().next() {
                    Some(val) => val,
                    None => return Err(ParseError::NotComplete),
                }?;
            }

            match line.name.as_str() {
                "END" => break,
                "BEGIN" => match line.value {
                    Some(v) => self.add_sub_component(v.as_str(), line_parser)?,
                    None    => self.add_sub_component("", line_parser)?,
                },

                _ => self.add_property(line),
            };
        }

        Ok(())
    }
}

#[derive(Debug)]
pub enum ParseError {
    Parse(parser::ParseError),
    EndOfFile,
    MissingHeader,
    NotImplemented,
    NotComplete,
    InvalidComponent(String),
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::MissingHeader => "Missing VCALENDAR header.",
            ParseError::EndOfFile => "End of file.",
            ParseError::NotImplemented => "Element parsing not implemented yet.",
            ParseError::NotComplete => "Calendar component is not complete.",
            ParseError::InvalidComponent(_) => "Contains an invalid component.",
            ParseError::Parse(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::Parse(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl From<parser::ParseError> for ParseError {
    fn from(err: parser::ParseError) -> ParseError {
        ParseError::Parse(err)
    }
}
<commit_msg>Do not allow "BEGIN:" (value-less begin)<commit_after>
pub mod ical;
pub mod vcard;

use std::io::BufRead;
use std::cell::RefCell;
use std::error::Error;
use std::fmt;

use line::parser;

pub trait Component {
    fn add_sub_component<B: BufRead>(&mut self,
                                     value: &str,
                                     line_parser: &RefCell<parser::LineParser<B>>)
                                     -> Result<(), ParseError>;
    fn add_property(&mut self, property: parser::LineParsed);

    fn parse<B: BufRead>(&mut self,
                         line_parser: &RefCell<parser::LineParser<B>>)
                         -> Result<(), ParseError> {

        loop {
            let line: parser::LineParsed;

            {
                line = match line_parser.borrow_mut().next() {
                    Some(val) => val,
                    None => return Err(ParseError::NotComplete),
                }?;
            }

            match line.name.as_str() {
                "END" => break,
                "BEGIN" => match line.value {
                    Some(v) => self.add_sub_component(v.as_str(), line_parser)?,
                    None    => return Err(ParseError::NotComplete),
                },

                _ => self.add_property(line),
            };
        }

        Ok(())
    }
}

#[derive(Debug)]
pub enum ParseError {
    Parse(parser::ParseError),
    EndOfFile,
    MissingHeader,
    NotImplemented,
    NotComplete,
    InvalidComponent(String),
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::MissingHeader => "Missing VCALENDAR header.",
            ParseError::EndOfFile => "End of file.",
            ParseError::NotImplemented => "Element parsing not implemented yet.",
            ParseError::NotComplete => "Calendar component is not complete.",
            ParseError::InvalidComponent(_) => "Contains an invalid component.",
            ParseError::Parse(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::Parse(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl From<parser::ParseError> for ParseError {
    fn from(err: parser::ParseError) -> ParseError {
        ParseError::Parse(err)
    }
}
<|endoftext|>