diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2013-05-17 10:45:09 -0700 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2013-05-22 21:57:05 -0700 |
| commit | 0c820d4123c754522b0655e9e74f692c55685bfa (patch) | |
| tree | 7dbb86c30b451217b4e8f75173043744fe3255ff /src/libstd | |
| parent | 565942b145efbf6c1d1f66db46423d721b55d32c (diff) | |
| download | rust-0c820d4123c754522b0655e9e74f692c55685bfa.tar.gz rust-0c820d4123c754522b0655e9e74f692c55685bfa.zip | |
libstd: Rename libcore to libstd and libstd to libextra; update makefiles.
This only changes the directory names; it does not change the "real" metadata names.
Diffstat (limited to 'src/libstd')
168 files changed, 52668 insertions, 32134 deletions
diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs deleted file mode 100644 index 9c7db35f6f2..00000000000 --- a/src/libstd/arc.rs +++ /dev/null @@ -1,803 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * Concurrency-enabled mechanisms for sharing mutable and/or immutable state - * between tasks. - * - * # Example - * - * In this example, a large vector of floats is shared between several tasks. - * With simple pipes, without ARC, a copy would have to be made for each task. - * - * ~~~ - * extern mod std; - * use std::arc; - * let numbers=vec::from_fn(100, |ind| (ind as float)*rand::random()); - * let shared_numbers=arc::ARC(numbers); - * - * for 10.times { - * let (port, chan) = stream(); - * chan.send(shared_numbers.clone()); - * - * do spawn { - * let shared_numbers=port.recv(); - * let local_numbers=shared_numbers.get(); - * - * // Work with the local numbers - * } - * } - * ~~~ - */ - -use sync; -use sync::{Mutex, mutex_with_condvars, RWlock, rwlock_with_condvars}; - -use core::cast; -use core::unstable::sync::UnsafeAtomicRcBox; -use core::ptr; -use core::task; - -/// As sync::condvar, a mechanism for unlock-and-descheduling and signaling. -pub struct Condvar<'self> { - is_mutex: bool, - failed: &'self mut bool, - cond: &'self sync::Condvar<'self> -} - -pub impl<'self> Condvar<'self> { - /// Atomically exit the associated ARC and block until a signal is sent. - #[inline(always)] - fn wait(&self) { self.wait_on(0) } - - /** - * Atomically exit the associated ARC and block on a specified condvar - * until a signal is sent on that same condvar (as sync::cond.wait_on). - * - * wait() is equivalent to wait_on(0). - */ - #[inline(always)] - fn wait_on(&self, condvar_id: uint) { - assert!(!*self.failed); - self.cond.wait_on(condvar_id); - // 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. - #[inline(always)] - fn signal(&self) -> bool { self.signal_on(0) } - - /** - * Wake up a blocked task on a specified condvar (as - * sync::cond.signal_on). Returns false if there was no blocked task. - */ - #[inline(always)] - fn signal_on(&self, condvar_id: uint) -> bool { - assert!(!*self.failed); - self.cond.signal_on(condvar_id) - } - - /// Wake up all blocked tasks. Returns the number of tasks woken. - #[inline(always)] - fn broadcast(&self) -> uint { self.broadcast_on(0) } - - /** - * Wake up all blocked tasks on a specified condvar (as - * sync::cond.broadcast_on). Returns Returns the number of tasks woken. - */ - #[inline(always)] - fn broadcast_on(&self, condvar_id: uint) -> uint { - assert!(!*self.failed); - self.cond.broadcast_on(condvar_id) - } -} - -/**************************************************************************** - * Immutable ARC - ****************************************************************************/ - -/// An atomically reference counted wrapper for shared immutable state. -struct ARC<T> { x: UnsafeAtomicRcBox<T> } - -/// Create an atomically reference counted wrapper. -pub fn ARC<T:Const + Owned>(data: T) -> ARC<T> { - ARC { x: UnsafeAtomicRcBox::new(data) } -} - -/** - * Access the underlying data in an atomically reference counted - * wrapper. - */ -pub fn get<'a, T:Const + Owned>(rc: &'a ARC<T>) -> &'a T { - rc.get() -} - -impl<T:Const+Owned> ARC<T> { - pub fn get<'a>(&'a self) -> &'a T { - unsafe { &*self.x.get_immut() } - } -} -/** - * 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. - */ -pub fn clone<T:Const + Owned>(rc: &ARC<T>) -> ARC<T> { - ARC { x: rc.x.clone() } -} - -impl<T:Const + Owned> Clone for ARC<T> { - fn clone(&self) -> ARC<T> { - clone(self) - } -} - -/**************************************************************************** - * Mutex protected ARC (unsafe) - ****************************************************************************/ - -#[doc(hidden)] -struct MutexARCInner<T> { lock: Mutex, failed: bool, data: T } -/// An ARC with mutable data protected by a blocking mutex. -struct MutexARC<T> { x: UnsafeAtomicRcBox<MutexARCInner<T>> } - -/// Create a mutex-protected ARC with the supplied data. -pub fn MutexARC<T:Owned>(user_data: T) -> MutexARC<T> { - mutex_arc_with_condvars(user_data, 1) -} -/** - * Create a mutex-protected ARC with the supplied data and a specified number - * of condvars (as sync::mutex_with_condvars). - */ -pub fn mutex_arc_with_condvars<T:Owned>(user_data: T, - num_condvars: uint) -> MutexARC<T> { - let data = - MutexARCInner { lock: mutex_with_condvars(num_condvars), - failed: false, data: user_data }; - MutexARC { x: UnsafeAtomicRcBox::new(data) } -} - -impl<T:Owned> Clone for MutexARC<T> { - /// Duplicate a mutex-protected ARC, as arc::clone. - fn clone(&self) -> MutexARC<T> { - // NB: Cloning the underlying mutex is not necessary. Its reference - // count would be exactly the same as the shared state's. - MutexARC { x: self.x.clone() } - } -} - -pub impl<T:Owned> MutexARC<T> { - - /** - * 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>(&self, blk: &fn(x: &mut T) -> U) -> U { - let state = self.x.get(); - // 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 = PoisonOnFail(&mut (*state).failed); - blk(&mut (*state).data) - } - } - - /// As access(), but with a condvar, as sync::mutex.lock_cond(). - #[inline(always)] - unsafe fn access_cond<'x, 'c, U>( - &self, - blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) -> U - { - let state = self.x.get(); - do (&(*state).lock).lock_cond |cond| { - check_poison(true, (*state).failed); - let _z = PoisonOnFail(&mut (*state).failed); - blk(&mut (*state).data, - &Condvar {is_mutex: true, - failed: &mut (*state).failed, - cond: cond }) - } - } -} - -// Common code for {mutex.access,rwlock.write}{,_cond}. -#[inline(always)] -#[doc(hidden)] -fn check_poison(is_mutex: bool, failed: bool) { - if failed { - if is_mutex { - fail!("Poisoned MutexARC - another task failed inside!"); - } else { - fail!("Poisoned rw_arc - another task failed inside!"); - } - } -} - -#[doc(hidden)] -struct PoisonOnFail { - failed: *mut bool, -} - -impl Drop for PoisonOnFail { - fn finalize(&self) { - unsafe { - /* assert!(!*self.failed); - -- might be false in case of cond.wait() */ - if task::failing() { - *self.failed = true; - } - } - } -} - -fn PoisonOnFail<'r>(failed: &'r mut bool) -> PoisonOnFail { - PoisonOnFail { - failed: ptr::to_mut_unsafe_ptr(failed) - } -} - -/**************************************************************************** - * R/W lock protected ARC - ****************************************************************************/ - -#[doc(hidden)] -struct RWARCInner<T> { 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. - */ -#[mutable] -struct RWARC<T> { - x: UnsafeAtomicRcBox<RWARCInner<T>>, - cant_nest: () -} - -/// Create a reader/writer ARC with the supplied data. -pub fn RWARC<T:Const + Owned>(user_data: T) -> RWARC<T> { - rw_arc_with_condvars(user_data, 1) -} -/** - * Create a reader/writer ARC with the supplied data and a specified number - * of condvars (as sync::rwlock_with_condvars). - */ -pub fn rw_arc_with_condvars<T:Const + Owned>( - user_data: T, - num_condvars: uint) -> RWARC<T> -{ - let data = - RWARCInner { lock: rwlock_with_condvars(num_condvars), - failed: false, data: user_data }; - RWARC { x: UnsafeAtomicRcBox::new(data), cant_nest: () } -} - -pub impl<T:Const + Owned> RWARC<T> { - /// Duplicate a rwlock-protected ARC, as arc::clone. - fn clone(&self) -> RWARC<T> { - RWARC { x: self.x.clone(), - cant_nest: () } - } - -} - -pub impl<T:Const + Owned> RWARC<T> { - /** - * 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 MutexARC.access, it will also - * poison the ARC, so subsequent readers and writers will both also fail. - */ - #[inline(always)] - fn write<U>(&self, blk: &fn(x: &mut T) -> U) -> U { - unsafe { - let state = self.x.get(); - do (*borrow_rwlock(state)).write { - check_poison(false, (*state).failed); - let _z = PoisonOnFail(&mut (*state).failed); - blk(&mut (*state).data) - } - } - } - /// As write(), but with a condvar, as sync::rwlock.write_cond(). - #[inline(always)] - fn write_cond<'x, 'c, U>(&self, - blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) - -> U { - unsafe { - let state = self.x.get(); - do (*borrow_rwlock(state)).write_cond |cond| { - check_poison(false, (*state).failed); - let _z = PoisonOnFail(&mut (*state).failed); - blk(&mut (*state).data, - &Condvar {is_mutex: false, - failed: &mut (*state).failed, - cond: cond}) - } - } - } - /** - * 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>(&self, blk: &fn(x: &T) -> U) -> U { - let state = self.x.get(); - unsafe { - do (*state).lock.read { - check_poison(false, (*state).failed); - blk(&(*state).data) - } - } - } - - /** - * As write(), but with the ability to atomically 'downgrade' the lock. - * See sync::rwlock.write_downgrade(). The RWWriteMode token must be used - * to obtain the &mut T, and can be transformed into a RWReadMode token by - * calling downgrade(), after which a &T can be obtained instead. - * ~~~ - * do arc.write_downgrade |write_mode| { - * do (&write_mode).write_cond |state, condvar| { - * ... exclusive access with mutable state ... - * } - * let read_mode = arc.downgrade(write_mode); - * do (&read_mode).read |state| { - * ... shared access with immutable state ... - * } - * } - * ~~~ - */ - fn write_downgrade<U>(&self, blk: &fn(v: RWWriteMode<T>) -> U) -> U { - unsafe { - let state = self.x.get(); - do (*borrow_rwlock(state)).write_downgrade |write_mode| { - check_poison(false, (*state).failed); - blk(RWWriteMode { - data: &mut (*state).data, - token: write_mode, - poison: PoisonOnFail(&mut (*state).failed) - }) - } - } - } - - /// To be called inside of the write_downgrade block. - fn downgrade<'a>(&self, token: RWWriteMode<'a, T>) -> RWReadMode<'a, T> { - unsafe { - // The rwlock should assert that the token belongs to us for us. - let state = self.x.get(); - let RWWriteMode { - data: data, - token: t, - poison: _poison - } = token; - // Let readers in - let new_token = (*state).lock.downgrade(t); - // Whatever region the input reference had, it will be safe to use - // the same region for the output reference. (The only 'unsafe' part - // of this cast is removing the mutability.) - let new_data = cast::transmute_immut(data); - // Downgrade ensured the token belonged to us. Just a sanity check. - assert!(ptr::ref_eq(&(*state).data, new_data)); - // Produce new token - RWReadMode { - data: new_data, - token: new_token, - } - } - } -} - -// 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'. -#[doc(hidden)] -fn borrow_rwlock<T:Const + Owned>(state: *const RWARCInner<T>) -> *RWlock { - unsafe { cast::transmute(&const (*state).lock) } -} - -/// The "write permission" token used for RWARC.write_downgrade(). -pub struct RWWriteMode<'self, T> { - data: &'self mut T, - token: sync::RWlockWriteMode<'self>, - poison: PoisonOnFail, -} - -/// The "read permission" token used for RWARC.write_downgrade(). -pub struct RWReadMode<'self, T> { - data: &'self T, - token: sync::RWlockReadMode<'self>, -} - -pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> { - /// Access the pre-downgrade RWARC in write mode. - fn write<U>(&mut self, blk: &fn(x: &mut T) -> U) -> U { - match *self { - RWWriteMode { - data: &ref mut data, - token: ref token, - poison: _ - } => { - do token.write { - blk(data) - } - } - } - } - /// Access the pre-downgrade RWARC in write mode with a condvar. - fn write_cond<'x, 'c, U>(&mut self, - blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) - -> U { - match *self { - RWWriteMode { - data: &ref mut data, - token: ref token, - poison: ref poison - } => { - do token.write_cond |cond| { - unsafe { - let cvar = Condvar { - is_mutex: false, - failed: &mut *poison.failed, - cond: cond - }; - blk(data, &cvar) - } - } - } - } - } -} - -pub impl<'self, T:Const + Owned> RWReadMode<'self, T> { - /// Access the post-downgrade rwlock in read mode. - fn read<U>(&self, blk: &fn(x: &T) -> U) -> U { - match *self { - RWReadMode { - data: data, - token: ref token - } => { - do token.read { blk(data) } - } - } - } -} - -/**************************************************************************** - * Tests - ****************************************************************************/ - -#[cfg(test)] -mod tests { - - use arc::*; - use arc; - - use core::cell::Cell; - use core::task; - - #[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, c) = comm::stream(); - - do task::spawn() || { - let p = comm::PortSet::new(); - c.send(p.chan()); - - let arc_v = p.recv(); - - let v = copy *arc::get::<~[int]>(&arc_v); - assert_eq!(v[3], 4); - }; - - let c = p.recv(); - c.send(arc::clone(&arc_v)); - - assert_eq!((*arc::get(&arc_v))[2], 3); - assert_eq!(arc_v.get()[4], 5); - - info!(arc_v); - } - - #[test] - fn test_mutex_arc_condvar() { - let arc = ~MutexARC(false); - let arc2 = ~arc.clone(); - let (p,c) = comm::oneshot(); - let (c,p) = (Cell(c), Cell(p)); - do task::spawn || { - // wait until parent gets in - comm::recv_one(p.take()); - do arc2.access_cond |state, cond| { - *state = true; - cond.signal(); - } - } - do arc.access_cond |state, cond| { - comm::send_one(c.take(), ()); - assert!(!*state); - while !*state { - cond.wait(); - } - } - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_arc_condvar_poison() { - let arc = ~MutexARC(1); - let arc2 = ~arc.clone(); - let (p, c) = comm::stream(); - - do task::spawn_unlinked || { - let _ = p.recv(); - do arc2.access_cond |one, cond| { - cond.signal(); - // Parent should fail when it wakes up. - assert_eq!(*one, 0); - } - } - - 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 = ~MutexARC(1); - let arc2 = ~arc.clone(); - do task::try || { - do arc2.access |one| { - assert_eq!(*one, 2); - } - }; - do arc.access |one| { - assert_eq!(*one, 1); - } - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_rw_arc_poison_wr() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.write |one| { - assert_eq!(*one, 2); - } - }; - do arc.read |one| { - assert_eq!(*one, 1); - } - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_rw_arc_poison_ww() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.write |one| { - assert_eq!(*one, 2); - } - }; - do arc.write |one| { - assert_eq!(*one, 1); - } - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_rw_arc_poison_dw() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.write_downgrade |mut write_mode| { - do write_mode.write |one| { - assert_eq!(*one, 2); - } - } - }; - do arc.write |one| { - assert_eq!(*one, 1); - } - } - #[test] #[ignore(cfg(windows))] - fn test_rw_arc_no_poison_rr() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.read |one| { - assert_eq!(*one, 2); - } - }; - do arc.read |one| { - assert_eq!(*one, 1); - } - } - #[test] #[ignore(cfg(windows))] - fn test_rw_arc_no_poison_rw() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.read |one| { - assert_eq!(*one, 2); - } - }; - do arc.write |one| { - assert_eq!(*one, 1); - } - } - #[test] #[ignore(cfg(windows))] - fn test_rw_arc_no_poison_dr() { - let arc = ~RWARC(1); - let arc2 = (*arc).clone(); - do task::try || { - do arc2.write_downgrade |write_mode| { - let read_mode = arc2.downgrade(write_mode); - do (&read_mode).read |one| { - assert_eq!(*one, 2); - } - } - }; - do arc.write |one| { - assert_eq!(*one, 1); - } - } - #[test] - fn test_rw_arc() { - let arc = ~RWARC(0); - let arc2 = (*arc).clone(); - let (p,c) = comm::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(); - let mut builder = task::task(); - builder.future_result(|r| children.push(r)); - do builder.spawn { - do arc3.read |num| { - assert!(*num >= 0); - } - } - } - - // Wait for children to pass their asserts - for children.each |r| { - r.recv(); - } - - // Wait for writer to finish - p.recv(); - do arc.read |num| { - assert_eq!(*num, 10); - } - } - #[test] - fn test_rw_downgrade() { - // (1) A downgrader gets in write mode and does cond.wait. - // (2) A writer gets in write mode, sets state to 42, and does signal. - // (3) Downgrader wakes, sets state to 31337. - // (4) tells writer and all other readers to contend as it downgrades. - // (5) Writer attempts to set state back to 42, while downgraded task - // and all reader tasks assert that it's 31337. - let arc = ~RWARC(0); - - // Reader tasks - let mut reader_convos = ~[]; - for 10.times { - let ((rp1,rc1),(rp2,rc2)) = (comm::stream(),comm::stream()); - reader_convos.push((rc1, rp2)); - let arcn = (*arc).clone(); - do task::spawn || { - rp1.recv(); // wait for downgrader to give go-ahead - do arcn.read |state| { - assert_eq!(*state, 31337); - rc2.send(()); - } - } - } - - // Writer task - let arc2 = (*arc).clone(); - let ((wp1,wc1),(wp2,wc2)) = (comm::stream(),comm::stream()); - do task::spawn || { - wp1.recv(); - do arc2.write_cond |state, cond| { - assert_eq!(*state, 0); - *state = 42; - cond.signal(); - } - wp1.recv(); - do arc2.write |state| { - // This shouldn't happen until after the downgrade read - // section, and all other readers, finish. - assert_eq!(*state, 31337); - *state = 42; - } - wc2.send(()); - } - - // Downgrader (us) - do arc.write_downgrade |mut write_mode| { - do write_mode.write_cond |state, cond| { - wc1.send(()); // send to another writer who will wake us up - while *state == 0 { - cond.wait(); - } - assert_eq!(*state, 42); - *state = 31337; - // send to other readers - for reader_convos.each |x| { - match *x { - (ref rc, _) => rc.send(()), - } - } - } - let read_mode = arc.downgrade(write_mode); - do (&read_mode).read |state| { - // complete handshake with other readers - for reader_convos.each |x| { - match *x { - (_, ref rp) => rp.recv(), - } - } - wc1.send(()); // tell writer to try again - assert_eq!(*state, 31337); - } - } - - wp2.recv(); // complete handshake with writer - } -} diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs deleted file mode 100644 index a087a16caa2..00000000000 --- a/src/libstd/arena.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Dynamic arenas. - -// Arenas are used to quickly allocate objects that share a -// lifetime. The arena uses ~[u8] vectors as a backing store to -// allocate objects from. For each allocated object, the arena stores -// a pointer to the type descriptor followed by the -// object. (Potentially with alignment padding after each of them.) -// When the arena is destroyed, it iterates through all of its chunks, -// and uses the tydesc information to trace through the objects, -// calling the destructors on them. -// One subtle point that needs to be addressed is how to handle -// failures while running the user provided initializer function. It -// is important to not run the destructor on uninitialized objects, but -// how to detect them is somewhat subtle. Since alloc() can be invoked -// recursively, it is not sufficient to simply exclude the most recent -// object. To solve this without requiring extra space, we use the low -// order bit of the tydesc pointer to encode whether the object it -// describes has been fully initialized. - -// As an optimization, objects with destructors are stored in -// different chunks than objects without destructors. This reduces -// overhead when initializing plain-old-data and means we don't need -// to waste time running the destructors of POD. - -use list::{MutList, MutCons, MutNil}; - -use core::at_vec; -use core::cast::{transmute, transmute_mut_region}; -use core::cast; -use core::libc::size_t; -use core::ptr; -use core::sys::TypeDesc; -use core::sys; -use core::uint; -use core::vec; -use core::unstable::intrinsics; - -pub mod rustrt { - use core::libc::size_t; - use core::sys::TypeDesc; - - pub extern { - #[rust_stack] - unsafe fn rust_call_tydesc_glue(root: *u8, - tydesc: *TypeDesc, - field: size_t); - } -} - -// This probably belongs somewhere else. Needs to be kept in sync with -// changes to glue... -static tydesc_drop_glue_index: size_t = 3 as size_t; - -// The way arena uses arrays is really deeply awful. The arrays are -// allocated, and have capacities reserved, but the fill for the array -// will always stay at 0. -struct Chunk { - data: @[u8], - fill: uint, - is_pod: bool, -} - -pub struct Arena { - // The head is separated out from the list as a unbenchmarked - // microoptimization, to avoid needing to case on the list to - // access the head. - priv head: Chunk, - priv pod_head: Chunk, - priv chunks: @mut MutList<Chunk>, -} - -#[unsafe_destructor] -impl Drop for Arena { - fn finalize(&self) { - unsafe { - destroy_chunk(&self.head); - for self.chunks.each |chunk| { - if !chunk.is_pod { - destroy_chunk(chunk); - } - } - } - } -} - -fn chunk(size: uint, is_pod: bool) -> Chunk { - let mut v: @[u8] = @[]; - unsafe { at_vec::raw::reserve(&mut v, size); } - Chunk { - data: unsafe { cast::transmute(v) }, - fill: 0u, - is_pod: is_pod, - } -} - -pub fn arena_with_size(initial_size: uint) -> Arena { - Arena { - head: chunk(initial_size, false), - pod_head: chunk(initial_size, true), - chunks: @mut MutNil, - } -} - -pub fn Arena() -> Arena { - arena_with_size(32u) -} - -#[inline(always)] -fn round_up_to(base: uint, align: uint) -> uint { - (base + (align - 1)) & !(align - 1) -} - -// Walk down a chunk, running the destructors for any objects stored -// in it. -unsafe fn destroy_chunk(chunk: &Chunk) { - let mut idx = 0; - let buf = vec::raw::to_ptr(chunk.data); - let fill = chunk.fill; - - while idx < fill { - let tydesc_data: *uint = transmute(ptr::offset(buf, idx)); - let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data); - let size = (*tydesc).size, align = (*tydesc).align; - - let after_tydesc = idx + sys::size_of::<*TypeDesc>(); - - let start = round_up_to(after_tydesc, align); - - //debug!("freeing object: idx = %u, size = %u, align = %u, done = %b", - // start, size, align, is_done); - if is_done { - rustrt::rust_call_tydesc_glue( - ptr::offset(buf, start), tydesc, tydesc_drop_glue_index); - } - - // Find where the next tydesc lives - idx = round_up_to(start + size, sys::pref_align_of::<*TypeDesc>()); - } -} - -// We encode whether the object a tydesc describes has been -// initialized in the arena in the low bit of the tydesc pointer. This -// is necessary in order to properly do cleanup if a failure occurs -// during an initializer. -#[inline(always)] -unsafe fn bitpack_tydesc_ptr(p: *TypeDesc, is_done: bool) -> uint { - let p_bits: uint = transmute(p); - p_bits | (is_done as uint) -} -#[inline(always)] -unsafe fn un_bitpack_tydesc_ptr(p: uint) -> (*TypeDesc, bool) { - (transmute(p & !1), p & 1 == 1) -} - -pub impl Arena { - // Functions for the POD part of the arena - priv fn alloc_pod_grow(&mut self, n_bytes: uint, align: uint) -> *u8 { - // Allocate a new chunk. - let chunk_size = at_vec::capacity(self.pod_head.data); - let new_min_chunk_size = uint::max(n_bytes, chunk_size); - self.chunks = @mut MutCons(copy self.pod_head, self.chunks); - self.pod_head = - chunk(uint::next_power_of_two(new_min_chunk_size + 1u), true); - - return self.alloc_pod_inner(n_bytes, align); - } - - #[inline(always)] - priv fn alloc_pod_inner(&mut self, n_bytes: uint, align: uint) -> *u8 { - unsafe { - // XXX: Borrow check - let head = transmute_mut_region(&mut self.pod_head); - - let start = round_up_to(head.fill, align); - let end = start + n_bytes; - if end > at_vec::capacity(head.data) { - return self.alloc_pod_grow(n_bytes, align); - } - head.fill = end; - - //debug!("idx = %u, size = %u, align = %u, fill = %u", - // start, n_bytes, align, head.fill); - - ptr::offset(vec::raw::to_ptr(head.data), start) - } - } - - #[inline(always)] - priv fn alloc_pod<'a, T>(&'a mut self, op: &fn() -> T) -> &'a T { - unsafe { - let tydesc = sys::get_type_desc::<T>(); - let ptr = self.alloc_pod_inner((*tydesc).size, (*tydesc).align); - let ptr: *mut T = transmute(ptr); - intrinsics::move_val_init(&mut (*ptr), op()); - return transmute(ptr); - } - } - - // Functions for the non-POD part of the arena - priv fn alloc_nonpod_grow(&mut self, n_bytes: uint, align: uint) - -> (*u8, *u8) { - // Allocate a new chunk. - let chunk_size = at_vec::capacity(self.head.data); - let new_min_chunk_size = uint::max(n_bytes, chunk_size); - self.chunks = @mut MutCons(copy self.head, self.chunks); - self.head = - chunk(uint::next_power_of_two(new_min_chunk_size + 1u), false); - - return self.alloc_nonpod_inner(n_bytes, align); - } - - #[inline(always)] - priv fn alloc_nonpod_inner(&mut self, n_bytes: uint, align: uint) - -> (*u8, *u8) { - unsafe { - let head = transmute_mut_region(&mut self.head); - - let tydesc_start = head.fill; - let after_tydesc = head.fill + sys::size_of::<*TypeDesc>(); - let start = round_up_to(after_tydesc, align); - let end = start + n_bytes; - if end > at_vec::capacity(head.data) { - return self.alloc_nonpod_grow(n_bytes, align); - } - head.fill = round_up_to(end, sys::pref_align_of::<*TypeDesc>()); - - //debug!("idx = %u, size = %u, align = %u, fill = %u", - // start, n_bytes, align, head.fill); - - let buf = vec::raw::to_ptr(head.data); - return (ptr::offset(buf, tydesc_start), ptr::offset(buf, start)); - } - } - - #[inline(always)] - priv fn alloc_nonpod<'a, T>(&'a mut self, op: &fn() -> T) -> &'a T { - unsafe { - let tydesc = sys::get_type_desc::<T>(); - let (ty_ptr, ptr) = - self.alloc_nonpod_inner((*tydesc).size, (*tydesc).align); - let ty_ptr: *mut uint = transmute(ty_ptr); - let ptr: *mut T = transmute(ptr); - // Write in our tydesc along with a bit indicating that it - // has *not* been initialized yet. - *ty_ptr = transmute(tydesc); - // Actually initialize it - intrinsics::move_val_init(&mut(*ptr), op()); - // Now that we are done, update the tydesc to indicate that - // the object is there. - *ty_ptr = bitpack_tydesc_ptr(tydesc, true); - - return transmute(ptr); - } - } - - // The external interface - #[inline(always)] - fn alloc<'a, T>(&'a mut self, op: &fn() -> T) -> &'a T { - unsafe { - // XXX: Borrow check - let this = transmute_mut_region(self); - if !intrinsics::needs_drop::<T>() { - return this.alloc_pod(op); - } - // XXX: Borrow check - let this = transmute_mut_region(self); - this.alloc_nonpod(op) - } - } -} - -#[test] -fn test_arena_destructors() { - let mut arena = Arena(); - for uint::range(0, 10) |i| { - // Arena allocate something with drop glue to make sure it - // doesn't leak. - do arena.alloc { @i }; - // Allocate something with funny size and alignment, to keep - // things interesting. - do arena.alloc { [0u8, 1u8, 2u8] }; - } -} - -#[test] -#[should_fail] -#[ignore(cfg(windows))] -fn test_arena_destructors_fail() { - let mut arena = Arena(); - // Put some stuff in the arena. - for uint::range(0, 10) |i| { - // Arena allocate something with drop glue to make sure it - // doesn't leak. - do arena.alloc { @i }; - // Allocate something with funny size and alignment, to keep - // things interesting. - do arena.alloc { [0u8, 1u8, 2u8] }; - } - // Now, fail while allocating - do arena.alloc::<@int> { - // Now fail. - fail!(); - }; -} diff --git a/src/libstd/at_vec.rs b/src/libstd/at_vec.rs new file mode 100644 index 00000000000..44c55563ac5 --- /dev/null +++ b/src/libstd/at_vec.rs @@ -0,0 +1,323 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Managed vectors + +use cast::transmute; +use container::Container; +use kinds::Copy; +use old_iter; +use old_iter::BaseIter; +use option::Option; +use sys; +use uint; +use vec; + +/// Code for dealing with @-vectors. This is pretty incomplete, and +/// contains a bunch of duplication from the code for ~-vectors. + +pub mod rustrt { + use libc; + use sys; + use vec; + + #[abi = "cdecl"] + #[link_name = "rustrt"] + pub extern { + pub unsafe fn vec_reserve_shared_actual(t: *sys::TypeDesc, + v: **vec::raw::VecRepr, + n: libc::size_t); + } +} + +/// Returns the number of elements the vector can hold without reallocating +#[inline(always)] +pub fn capacity<T>(v: @[T]) -> uint { + unsafe { + let repr: **raw::VecRepr = transmute(&v); + (**repr).unboxed.alloc / sys::size_of::<T>() + } +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * This version takes an initial size for the vector. + * + * # Arguments + * + * * size - An initial size of the vector to reserve + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build_sized<A>(size: uint, builder: &fn(push: &fn(v: A))) -> @[A] { + let mut vec: @[A] = @[]; + unsafe { raw::reserve(&mut vec, size); } + builder(|x| unsafe { raw::push(&mut vec, x) }); + return unsafe { transmute(vec) }; +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * + * # Arguments + * + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build<A>(builder: &fn(push: &fn(v: A))) -> @[A] { + build_sized(4, builder) +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * This version takes an initial size for the vector. + * + * # Arguments + * + * * size - An option, maybe containing initial size of the vector to reserve + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build_sized_opt<A>(size: Option<uint>, + builder: &fn(push: &fn(v: A))) + -> @[A] { + build_sized(size.get_or_default(4), builder) +} + +// Appending +#[inline(always)] +pub fn append<T:Copy>(lhs: @[T], rhs: &const [T]) -> @[T] { + do build_sized(lhs.len() + rhs.len()) |push| { + for lhs.each |x| { push(*x); } + for uint::range(0, rhs.len()) |i| { push(rhs[i]); } + } +} + + +/// Apply a function to each element of a vector and return the results +pub fn map<T, U>(v: &[T], f: &fn(x: &T) -> U) -> @[U] { + do build_sized(v.len()) |push| { + for v.each |elem| { + push(f(elem)); + } + } +} + +/** + * Creates and initializes an immutable vector. + * + * Creates an immutable vector of size `n_elts` and initializes the elements + * to the value returned by the function `op`. + */ +pub fn from_fn<T>(n_elts: uint, op: old_iter::InitOp<T>) -> @[T] { + do build_sized(n_elts) |push| { + let mut i: uint = 0u; + while i < n_elts { push(op(i)); i += 1u; } + } +} + +/** + * Creates and initializes an immutable vector. + * + * Creates an immutable vector of size `n_elts` and initializes the elements + * to the value `t`. + */ +pub fn from_elem<T:Copy>(n_elts: uint, t: T) -> @[T] { + do build_sized(n_elts) |push| { + let mut i: uint = 0u; + while i < n_elts { push(copy t); i += 1u; } + } +} + +/** + * Creates and initializes an immutable managed vector by moving all the + * elements from an owned vector. + */ +pub fn to_managed_consume<T>(v: ~[T]) -> @[T] { + let mut av = @[]; + unsafe { + raw::reserve(&mut av, v.len()); + do vec::consume(v) |_i, x| { + raw::push(&mut av, x); + } + transmute(av) + } +} + +/** + * Creates and initializes an immutable managed vector by copying all the + * elements of a slice. + */ +pub fn to_managed<T:Copy>(v: &[T]) -> @[T] { + from_fn(v.len(), |i| v[i]) +} + +#[cfg(not(test))] +pub mod traits { + use at_vec::append; + use kinds::Copy; + use ops::Add; + + impl<'self,T:Copy> Add<&'self const [T],@[T]> for @[T] { + #[inline(always)] + fn add(&self, rhs: & &'self const [T]) -> @[T] { + append(*self, (*rhs)) + } + } +} + +#[cfg(test)] +pub mod traits {} + +pub mod raw { + use at_vec::{capacity, rustrt}; + use cast::{transmute, transmute_copy}; + use libc; + use ptr; + use sys; + use uint; + use unstable::intrinsics::{move_val_init}; + use vec; + + pub type VecRepr = vec::raw::VecRepr; + pub type SliceRepr = vec::raw::SliceRepr; + + /** + * Sets the length of a vector + * + * This will explicitly set the size of the vector, without actually + * modifing its buffers, so it is up to the caller to ensure that + * the vector is actually the specified size. + */ + #[inline(always)] + pub unsafe fn set_len<T>(v: @[T], new_len: uint) { + let repr: **mut VecRepr = transmute(&v); + (**repr).unboxed.fill = new_len * sys::size_of::<T>(); + } + + #[inline(always)] + pub unsafe fn push<T>(v: &mut @[T], initval: T) { + let repr: **VecRepr = transmute_copy(&v); + let fill = (**repr).unboxed.fill; + if (**repr).unboxed.alloc > fill { + push_fast(v, initval); + } else { + push_slow(v, initval); + } + } + + #[inline(always)] // really pretty please + pub unsafe fn push_fast<T>(v: &mut @[T], initval: T) { + let repr: **mut VecRepr = ::cast::transmute(v); + let fill = (**repr).unboxed.fill; + (**repr).unboxed.fill += sys::size_of::<T>(); + let p = &((**repr).unboxed.data); + let p = ptr::offset(p, fill) as *mut T; + move_val_init(&mut(*p), initval); + } + + pub unsafe fn push_slow<T>(v: &mut @[T], initval: T) { + reserve_at_least(&mut *v, v.len() + 1u); + push_fast(v, initval); + } + + /** + * Reserves capacity for exactly `n` elements in the given vector. + * + * If the capacity for `v` is already equal to or greater than the + * requested capacity, then no action is taken. + * + * # Arguments + * + * * v - A vector + * * n - The number of elements to reserve space for + */ + pub unsafe fn reserve<T>(v: &mut @[T], n: uint) { + // Only make the (slow) call into the runtime if we have to + if capacity(*v) < n { + let ptr: **VecRepr = transmute(v); + rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(), + ptr, n as libc::size_t); + } + } + + /** + * Reserves capacity for at least `n` elements in the given vector. + * + * This function will over-allocate in order to amortize the + * allocation costs in scenarios where the caller may need to + * repeatedly reserve additional space. + * + * If the capacity for `v` is already equal to or greater than the + * requested capacity, then no action is taken. + * + * # Arguments + * + * * v - A vector + * * n - The number of elements to reserve space for + */ + pub unsafe fn reserve_at_least<T>(v: &mut @[T], n: uint) { + reserve(v, uint::next_power_of_two(n)); + } +} + +#[cfg(test)] +mod test { + use super::*; + use prelude::*; + + #[test] + fn test() { + // Some code that could use that, then: + fn seq_range(lo: uint, hi: uint) -> @[uint] { + do build |push| { + for uint::range(lo, hi) |i| { + push(i); + } + } + } + + assert_eq!(seq_range(10, 15), @[10, 11, 12, 13, 14]); + assert_eq!(from_fn(5, |x| x+1), @[1, 2, 3, 4, 5]); + assert_eq!(from_elem(5, 3.14), @[3.14, 3.14, 3.14, 3.14, 3.14]); + } + + #[test] + fn append_test() { + assert_eq!(@[1,2,3] + @[4,5,6], @[1,2,3,4,5,6]); + } + + #[test] + fn test_to_managed_consume() { + assert_eq!(to_managed_consume::<int>(~[]), @[]); + assert_eq!(to_managed_consume(~[true]), @[true]); + assert_eq!(to_managed_consume(~[1, 2, 3, 4, 5]), @[1, 2, 3, 4, 5]); + assert_eq!(to_managed_consume(~[~"abc", ~"123"]), @[~"abc", ~"123"]); + assert_eq!(to_managed_consume(~[~[42]]), @[~[42]]); + } + + #[test] + fn test_to_managed() { + assert_eq!(to_managed::<int>([]), @[]); + assert_eq!(to_managed([true]), @[true]); + assert_eq!(to_managed([1, 2, 3, 4, 5]), @[1, 2, 3, 4, 5]); + assert_eq!(to_managed([@"abc", @"123"]), @[@"abc", @"123"]); + assert_eq!(to_managed([@[42]]), @[@[42]]); + } +} diff --git a/src/libstd/base64.rs b/src/libstd/base64.rs deleted file mode 100644 index a820862ef4c..00000000000 --- a/src/libstd/base64.rs +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Base64 binary-to-text encoding - -pub trait ToBase64 { - fn to_base64(&self) -> ~str; -} - -static CHARS: [char, ..64] = [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' -]; - -impl<'self> ToBase64 for &'self [u8] { - /** - * Turn a vector of `u8` bytes into a base64 string. - * - * *Example*: - * - * ~~~~ - * extern mod std; - * use std::base64::ToBase64; - * - * fn main () { - * let str = [52,32].to_base64(); - * println(fmt!("%s", str)); - * } - * ~~~~ - */ - fn to_base64(&self) -> ~str { - let mut s = ~""; - let len = self.len(); - str::reserve(&mut s, ((len + 3u) / 4u) * 3u); - - let mut i = 0u; - - while i < len - (len % 3u) { - let n = (self[i] as uint) << 16u | - (self[i + 1u] as uint) << 8u | - (self[i + 2u] as uint); - - // This 24-bit number gets separated into four 6-bit numbers. - str::push_char(&mut s, CHARS[(n >> 18u) & 63u]); - str::push_char(&mut s, CHARS[(n >> 12u) & 63u]); - str::push_char(&mut s, CHARS[(n >> 6u) & 63u]); - str::push_char(&mut s, CHARS[n & 63u]); - - i += 3u; - } - - // Heh, would be cool if we knew this was exhaustive - // (the dream of bounded integer types) - match len % 3 { - 0 => (), - 1 => { - let n = (self[i] as uint) << 16u; - str::push_char(&mut s, CHARS[(n >> 18u) & 63u]); - str::push_char(&mut s, CHARS[(n >> 12u) & 63u]); - str::push_char(&mut s, '='); - str::push_char(&mut s, '='); - } - 2 => { - let n = (self[i] as uint) << 16u | - (self[i + 1u] as uint) << 8u; - str::push_char(&mut s, CHARS[(n >> 18u) & 63u]); - str::push_char(&mut s, CHARS[(n >> 12u) & 63u]); - str::push_char(&mut s, CHARS[(n >> 6u) & 63u]); - str::push_char(&mut s, '='); - } - _ => fail!("Algebra is broken, please alert the math police") - } - s - } -} - -impl<'self> ToBase64 for &'self str { - /** - * Convert any string (literal, `@`, `&`, or `~`) to base64 encoding. - * - * - * *Example*: - * - * ~~~~ - * extern mod std; - * use std::base64::ToBase64; - * - * fn main () { - * let str = "Hello, World".to_base64(); - * println(fmt!("%s",str)); - * } - * ~~~~ - * - */ - fn to_base64(&self) -> ~str { - str::to_bytes(*self).to_base64() - } -} - -pub trait FromBase64 { - fn from_base64(&self) -> ~[u8]; -} - -impl FromBase64 for ~[u8] { - /** - * Convert base64 `u8` vector into u8 byte values. - * Every 4 encoded characters is converted into 3 octets, modulo padding. - * - * *Example*: - * - * ~~~~ - * extern mod std; - * use std::base64::ToBase64; - * use std::base64::FromBase64; - * - * fn main () { - * let str = [52,32].to_base64(); - * println(fmt!("%s", str)); - * let bytes = str.from_base64(); - * println(fmt!("%?",bytes)); - * } - * ~~~~ - */ - fn from_base64(&self) -> ~[u8] { - if self.len() % 4u != 0u { fail!("invalid base64 length"); } - - let len = self.len(); - let mut padding = 0u; - - if len != 0u { - if self[len - 1u] == '=' as u8 { padding += 1u; } - if self[len - 2u] == '=' as u8 { padding += 1u; } - } - - let mut r = vec::with_capacity((len / 4u) * 3u - padding); - - let mut i = 0u; - while i < len { - let mut n = 0u; - - for 4u.times { - let ch = self[i] as char; - n <<= 6u; - - match ch { - 'A'..'Z' => n |= (ch as uint) - 0x41, - 'a'..'z' => n |= (ch as uint) - 0x47, - '0'..'9' => n |= (ch as uint) + 0x04, - '+' => n |= 0x3E, - '/' => n |= 0x3F, - '=' => { - match len - i { - 1u => { - r.push(((n >> 16u) & 0xFFu) as u8); - r.push(((n >> 8u ) & 0xFFu) as u8); - return copy r; - } - 2u => { - r.push(((n >> 10u) & 0xFFu) as u8); - return copy r; - } - _ => fail!("invalid base64 padding") - } - } - _ => fail!("invalid base64 character") - } - - i += 1u; - }; - - r.push(((n >> 16u) & 0xFFu) as u8); - r.push(((n >> 8u ) & 0xFFu) as u8); - r.push(((n ) & 0xFFu) as u8); - } - r - } -} - -impl FromBase64 for ~str { - /** - * Convert any base64 encoded string (literal, `@`, `&`, or `~`) - * to the byte values it encodes. - * - * You can use the `from_bytes` function in `core::str` - * to turn a `[u8]` into a string with characters corresponding to those values. - * - * *Example*: - * - * This converts a string literal to base64 and back. - * - * ~~~~ - * extern mod std; - * use std::base64::ToBase64; - * use std::base64::FromBase64; - * use core::str; - * - * fn main () { - * let hello_str = "Hello, World".to_base64(); - * println(fmt!("%s",hello_str)); - * let bytes = hello_str.from_base64(); - * println(fmt!("%?",bytes)); - * let result_str = str::from_bytes(bytes); - * println(fmt!("%s",result_str)); - * } - * ~~~~ - */ - fn from_base64(&self) -> ~[u8] { - str::to_bytes(*self).from_base64() - } -} - -#[cfg(test)] -mod tests { - use core::str; - - #[test] - fn test_to_base64() { - assert_eq!((~"").to_base64(), ~""); - assert!((~"f").to_base64() == ~"Zg=="); - assert_eq!((~"fo").to_base64(), ~"Zm8="); - assert_eq!((~"foo").to_base64(), ~"Zm9v"); - assert!((~"foob").to_base64() == ~"Zm9vYg=="); - assert_eq!((~"fooba").to_base64(), ~"Zm9vYmE="); - assert_eq!((~"foobar").to_base64(), ~"Zm9vYmFy"); - } - - #[test] - fn test_from_base64() { - assert_eq!((~"").from_base64(), str::to_bytes(~"")); - assert!((~"Zg==").from_base64() == str::to_bytes(~"f")); - assert_eq!((~"Zm8=").from_base64(), str::to_bytes(~"fo")); - assert_eq!((~"Zm9v").from_base64(), str::to_bytes(~"foo")); - assert!((~"Zm9vYg==").from_base64() == str::to_bytes(~"foob")); - assert_eq!((~"Zm9vYmE=").from_base64(), str::to_bytes(~"fooba")) - assert_eq!((~"Zm9vYmFy").from_base64(), str::to_bytes(~"foobar")); - } -} diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs deleted file mode 100644 index 9d22107931e..00000000000 --- a/src/libstd/bitv.rs +++ /dev/null @@ -1,1494 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 core::vec::from_elem; - -struct SmallBitv { - /// only the lowest nbits of this value are used. the rest is undefined. - bits: uint -} - -/// a mask that has a 1 for each defined bit in a small_bitv, assuming n bits -#[inline(always)] -fn small_mask(nbits: uint) -> uint { - (1 << nbits) - 1 -} - -pub impl SmallBitv { - fn new(bits: uint) -> SmallBitv { - SmallBitv {bits: bits} - } - - #[inline(always)] - fn bits_op(&mut self, right_bits: uint, nbits: uint, - f: &fn(uint, uint) -> uint) -> bool { - let mask = small_mask(nbits); - let old_b: uint = self.bits; - let new_b = f(old_b, right_bits); - self.bits = new_b; - mask & old_b != mask & new_b - } - - #[inline(always)] - fn union(&mut self, s: &SmallBitv, nbits: uint) -> bool { - self.bits_op(s.bits, nbits, |u1, u2| u1 | u2) - } - - #[inline(always)] - fn intersect(&mut self, s: &SmallBitv, nbits: uint) -> bool { - self.bits_op(s.bits, nbits, |u1, u2| u1 & u2) - } - - #[inline(always)] - fn become(&mut self, s: &SmallBitv, nbits: uint) -> bool { - self.bits_op(s.bits, nbits, |_u1, u2| u2) - } - - #[inline(always)] - fn difference(&mut self, s: &SmallBitv, nbits: uint) -> bool { - self.bits_op(s.bits, nbits, |u1, u2| u1 & !u2) - } - - #[inline(always)] - fn get(&self, i: uint) -> bool { - (self.bits & (1 << i)) != 0 - } - - #[inline(always)] - fn set(&mut self, i: uint, x: bool) { - if x { - self.bits |= 1<<i; - } - else { - self.bits &= !(1<<i as uint); - } - } - - #[inline(always)] - fn equals(&self, b: &SmallBitv, nbits: uint) -> bool { - let mask = small_mask(nbits); - mask & self.bits == mask & b.bits - } - - #[inline(always)] - fn clear(&mut self) { self.bits = 0; } - - #[inline(always)] - fn set_all(&mut self) { self.bits = !0; } - - #[inline(always)] - fn is_true(&self, nbits: uint) -> bool { - small_mask(nbits) & !self.bits == 0 - } - - #[inline(always)] - fn is_false(&self, nbits: uint) -> bool { - small_mask(nbits) & self.bits == 0 - } - - #[inline(always)] - fn invert(&mut self) { self.bits = !self.bits; } - -} - -struct BigBitv { - storage: ~[uint] -} - -/** - * a mask that has a 1 for each defined bit in the nth element of a big_bitv, - * assuming n bits. - */ -#[inline(always)] -fn big_mask(nbits: uint, elem: uint) -> uint { - let rmd = nbits % uint::bits; - let nelems = nbits/uint::bits + if rmd == 0 {0} else {1}; - - if elem < nelems - 1 || rmd == 0 { - !0 - } else { - (1 << rmd) - 1 - } -} - -pub impl BigBitv { - fn new(storage: ~[uint]) -> BigBitv { - BigBitv {storage: storage} - } - - #[inline(always)] - fn process(&mut self, b: &BigBitv, nbits: uint, - op: &fn(uint, uint) -> uint) -> bool { - let len = b.storage.len(); - assert_eq!(self.storage.len(), len); - let mut changed = false; - for uint::range(0, len) |i| { - let mask = big_mask(nbits, i); - let w0 = self.storage[i] & mask; - let w1 = b.storage[i] & mask; - let w = op(w0, w1) & mask; - if w0 != w { - changed = true; - self.storage[i] = w; - } - } - changed - } - - #[inline(always)] - fn each_storage(&mut self, op: &fn(v: &mut uint) -> bool) -> bool { - uint::range(0, self.storage.len(), |i| op(&mut self.storage[i])) - } - - #[inline(always)] - fn invert(&mut self) { for self.each_storage |w| { *w = !*w } } - - #[inline(always)] - fn union(&mut self, b: &BigBitv, nbits: uint) -> bool { - self.process(b, nbits, |w1, w2| w1 | w2) - } - - #[inline(always)] - fn intersect(&mut self, b: &BigBitv, nbits: uint) -> bool { - self.process(b, nbits, |w1, w2| w1 & w2) - } - - #[inline(always)] - fn become(&mut self, b: &BigBitv, nbits: uint) -> bool { - self.process(b, nbits, |_, w| w) - } - - #[inline(always)] - fn difference(&mut self, b: &BigBitv, nbits: uint) -> bool { - self.process(b, nbits, |w1, w2| w1 & !w2) - } - - #[inline(always)] - fn get(&self, i: uint) -> bool { - let w = i / uint::bits; - let b = i % uint::bits; - let x = 1 & self.storage[w] >> b; - x == 1 - } - - #[inline(always)] - fn set(&mut self, i: uint, x: bool) { - let w = i / uint::bits; - let b = i % uint::bits; - let flag = 1 << b; - self.storage[w] = if x { self.storage[w] | flag } - else { self.storage[w] & !flag }; - } - - #[inline(always)] - fn equals(&self, b: &BigBitv, nbits: uint) -> bool { - let len = b.storage.len(); - for uint::iterate(0, len) |i| { - let mask = big_mask(nbits, i); - if mask & self.storage[i] != mask & b.storage[i] { - return false; - } - } - return true; - } - -} - -enum BitvVariant { Big(~BigBitv), Small(~SmallBitv) } - -enum Op {Union, Intersect, Assign, Difference} - -// The bitvector type -pub struct Bitv { - rep: BitvVariant, - nbits: uint -} - -fn die() -> ! { - fail!("Tried to do operation on bit vectors with different sizes"); -} - -priv impl Bitv { - - #[inline(always)] - fn do_op(&mut self, op: Op, other: &Bitv) -> bool { - if self.nbits != other.nbits { - die(); - } - match self.rep { - Small(ref mut s) => match other.rep { - Small(ref s1) => match op { - Union => s.union(*s1, self.nbits), - Intersect => s.intersect(*s1, self.nbits), - Assign => s.become(*s1, self.nbits), - Difference => s.difference(*s1, self.nbits) - }, - Big(_) => die() - }, - Big(ref mut s) => match other.rep { - Small(_) => die(), - Big(ref s1) => match op { - Union => s.union(*s1, self.nbits), - Intersect => s.intersect(*s1, self.nbits), - Assign => s.become(*s1, self.nbits), - Difference => s.difference(*s1, self.nbits) - } - } - } - } - -} - -pub impl Bitv { - fn new(nbits: uint, init: bool) -> Bitv { - let rep = if nbits <= uint::bits { - Small(~SmallBitv::new(if init {!0} else {0})) - } - else { - let nelems = nbits/uint::bits + - if nbits % uint::bits == 0 {0} else {1}; - let elem = if init {!0} else {0}; - let s = from_elem(nelems, elem); - Big(~BigBitv::new(s)) - }; - Bitv {rep: rep, nbits: nbits} - } - - /** - * Calculates the union of two bitvectors - * - * Sets `self` to the union of `self` and `v1`. Both bitvectors must be - * the same length. Returns 'true' if `self` changed. - */ - #[inline(always)] - fn union(&mut self, v1: &Bitv) -> bool { self.do_op(Union, v1) } - - /** - * Calculates the intersection of two bitvectors - * - * Sets `self` to the intersection of `self` and `v1`. Both bitvectors - * must be the same length. Returns 'true' if `self` changed. - */ - #[inline(always)] - fn intersect(&mut self, v1: &Bitv) -> bool { self.do_op(Intersect, v1) } - - /** - * Assigns the value of `v1` to `self` - * - * Both bitvectors must be the same length. Returns `true` if `self` was - * changed - */ - #[inline(always)] - fn assign(&mut self, v: &Bitv) -> bool { self.do_op(Assign, v) } - - /// Retrieve the value at index `i` - #[inline(always)] - fn get(&self, i: uint) -> bool { - assert!((i < self.nbits)); - match self.rep { - Big(ref b) => b.get(i), - Small(ref s) => s.get(i) - } - } - - /** - * Set the value of a bit at a given index - * - * `i` must be less than the length of the bitvector. - */ - #[inline(always)] - fn set(&mut self, i: uint, x: bool) { - assert!((i < self.nbits)); - match self.rep { - Big(ref mut b) => b.set(i, x), - Small(ref mut s) => s.set(i, x) - } - } - - /** - * Compares two bitvectors - * - * Both bitvectors must be the same length. Returns `true` if both - * bitvectors contain identical elements. - */ - #[inline(always)] - fn equal(&self, v1: &Bitv) -> bool { - if self.nbits != v1.nbits { return false; } - match self.rep { - Small(ref b) => match v1.rep { - Small(ref b1) => b.equals(*b1, self.nbits), - _ => false - }, - Big(ref s) => match v1.rep { - Big(ref s1) => s.equals(*s1, self.nbits), - Small(_) => return false - } - } - } - - /// Set all bits to 0 - #[inline(always)] - fn clear(&mut self) { - match self.rep { - Small(ref mut b) => b.clear(), - Big(ref mut s) => for s.each_storage() |w| { *w = 0u } - } - } - - /// Set all bits to 1 - #[inline(always)] - fn set_all(&mut self) { - match self.rep { - Small(ref mut b) => b.set_all(), - Big(ref mut s) => for s.each_storage() |w| { *w = !0u } } - } - - /// Invert all bits - #[inline(always)] - fn invert(&mut self) { - match self.rep { - Small(ref mut b) => b.invert(), - Big(ref mut s) => for s.each_storage() |w| { *w = !*w } } - } - - /** - * Calculate the difference between two bitvectors - * - * Sets each element of `v0` to the value of that element minus the - * element of `v1` at the same index. Both bitvectors must be the same - * length. - * - * Returns `true` if `v0` was changed. - */ - #[inline(always)] - fn difference(&mut self, v: &Bitv) -> bool { self.do_op(Difference, v) } - - /// Returns true if all bits are 1 - #[inline(always)] - fn is_true(&self) -> bool { - match self.rep { - Small(ref b) => b.is_true(self.nbits), - _ => { - for self.each() |i| { if !i { return false; } } - true - } - } - } - - #[inline(always)] - fn each(&self, f: &fn(bool) -> bool) -> bool { - let mut i = 0; - while i < self.nbits { - if !f(self.get(i)) { return false; } - i += 1; - } - return true; - } - - /// Returns true if all bits are 0 - fn is_false(&self) -> bool { - match self.rep { - Small(ref b) => b.is_false(self.nbits), - Big(_) => { - for self.each() |i| { if i { return false; } } - true - } - } - } - - fn init_to_vec(&self, i: uint) -> uint { - return if self.get(i) { 1 } else { 0 }; - } - - /** - * Converts `self` to a vector of uint with the same length. - * - * Each uint in the resulting vector has either value 0u or 1u. - */ - fn to_vec(&self) -> ~[uint] { - vec::from_fn(self.nbits, |x| self.init_to_vec(x)) - } - - /** - * Organise the bits into bytes, such that the first bit in the - * bitv becomes the high-order bit of the first byte. If the - * size of the bitv is not a multiple of 8 then trailing bits - * will be filled-in with false/0 - */ - fn to_bytes(&self) -> ~[u8] { - - fn bit (bitv: &Bitv, byte: uint, bit: uint) -> u8 { - let offset = byte * 8 + bit; - if offset >= bitv.nbits { - 0 - } else { - bitv[offset] as u8 << (7 - bit) - } - } - - let len = self.nbits/8 + - if self.nbits % 8 == 0 { 0 } else { 1 }; - vec::from_fn(len, |i| - bit(self, i, 0) | - bit(self, i, 1) | - bit(self, i, 2) | - bit(self, i, 3) | - bit(self, i, 4) | - bit(self, i, 5) | - bit(self, i, 6) | - bit(self, i, 7) - ) - } - - /** - * Transform self into a [bool] by turning each bit into a bool - */ - fn to_bools(&self) -> ~[bool] { - vec::from_fn(self.nbits, |i| self[i]) - } - - /** - * Converts `self` to a string. - * - * The resulting string has the same length as `self`, and each - * character is either '0' or '1'. - */ - fn to_str(&self) -> ~str { - let mut rs = ~""; - for self.each() |i| { if i { rs += ~"1"; } else { rs += ~"0"; } }; - rs - } - - - /** - * Compare a bitvector to a vector of uint - * - * The uint vector is expected to only contain the values 0u and 1u. Both - * the bitvector and vector must have the same length - */ - fn eq_vec(&self, v: ~[uint]) -> bool { - assert_eq!(self.nbits, v.len()); - let mut i = 0; - while i < self.nbits { - let w0 = self.get(i); - let w1 = v[i]; - if !w0 && w1 != 0u || w0 && w1 == 0u { return false; } - i = i + 1; - } - true - } - - fn ones(&self, f: &fn(uint) -> bool) -> bool { - uint::range(0, self.nbits, |i| !self.get(i) || f(i)) - } - -} - -impl Clone for Bitv { - /// Makes a copy of a bitvector - #[inline(always)] - fn clone(&self) -> Bitv { - match self.rep { - Small(ref b) => { - Bitv{nbits: self.nbits, rep: Small(~SmallBitv{bits: b.bits})} - } - Big(ref b) => { - let mut st = from_elem(self.nbits / uint::bits + 1, 0); - let len = st.len(); - for uint::range(0, len) |i| { st[i] = b.storage[i]; }; - Bitv{nbits: self.nbits, rep: Big(~BigBitv{storage: st})} - } - } - } - -} - -/** - * Transform a byte-vector into a bitv. Each byte becomes 8 bits, - * with the most significant bits of each byte coming first. Each - * bit becomes true if equal to 1 or false if equal to 0. - */ -pub fn from_bytes(bytes: &[u8]) -> Bitv { - from_fn(bytes.len() * 8, |i| { - let b = bytes[i / 8] as uint; - let offset = i % 8; - b >> (7 - offset) & 1 == 1 - }) -} - -/** - * Transform a [bool] into a bitv by converting each bool into a bit. - */ -pub fn from_bools(bools: &[bool]) -> Bitv { - from_fn(bools.len(), |i| bools[i]) -} - -/** - * Create a bitv of the specified length where the value at each - * index is f(index). - */ -pub fn from_fn(len: uint, f: &fn(index: uint) -> bool) -> Bitv { - let mut bitv = Bitv::new(len, false); - for uint::range(0, len) |i| { - bitv.set(i, f(i)); - } - bitv -} - -impl ops::Index<uint,bool> for Bitv { - fn index(&self, i: &uint) -> bool { - self.get(*i) - } -} - -#[inline(always)] -fn iterate_bits(base: uint, bits: uint, f: &fn(uint) -> bool) -> bool { - if bits == 0 { - return true; - } - for uint::range(0, uint::bits) |i| { - if bits & (1 << i) != 0 { - if !f(base + i) { - return false; - } - } - } - return true; -} - -/// An implementation of a set using a bit vector as an underlying -/// representation for holding numerical elements. -/// -/// It should also be noted that the amount of storage necessary for holding a -/// set of objects is proportional to the maximum of the objects when viewed -/// as a uint. -pub struct BitvSet { - priv size: uint, - - // In theory this is a Bitv instead of always a BigBitv, but knowing that - // there's an array of storage makes our lives a whole lot easier when - // performing union/intersection/etc operations - priv bitv: BigBitv -} - -pub impl BitvSet { - /// Creates a new bit vector set with initially no contents - fn new() -> BitvSet { - BitvSet{ size: 0, bitv: BigBitv::new(~[0]) } - } - - /// Creates a new bit vector set from the given bit vector - fn from_bitv(bitv: Bitv) -> BitvSet { - let mut size = 0; - for bitv.ones |_| { - size += 1; - } - let Bitv{rep, _} = bitv; - match rep { - Big(~b) => BitvSet{ size: size, bitv: b }, - Small(~SmallBitv{bits}) => - BitvSet{ size: size, bitv: BigBitv{ storage: ~[bits] } }, - } - } - - /// Returns the capacity in bits for this bit vector. Inserting any - /// element less than this amount will not trigger a resizing. - fn capacity(&self) -> uint { self.bitv.storage.len() * uint::bits } - - /// Consumes this set to return the underlying bit vector - fn unwrap(self) -> Bitv { - let cap = self.capacity(); - let BitvSet{bitv, _} = self; - return Bitv{ nbits:cap, rep: Big(~bitv) }; - } - - #[inline(always)] - priv fn other_op(&mut self, other: &BitvSet, f: &fn(uint, uint) -> uint) { - fn nbits(mut w: uint) -> uint { - let mut bits = 0; - for uint::bits.times { - if w == 0 { - break; - } - bits += w & 1; - w >>= 1; - } - return bits; - } - if self.capacity() < other.capacity() { - self.bitv.storage.grow(other.capacity() / uint::bits, &0); - } - for other.bitv.storage.eachi |i, &w| { - let old = self.bitv.storage[i]; - let new = f(old, w); - self.bitv.storage[i] = new; - self.size += nbits(new) - nbits(old); - } - } - - /// Union in-place with the specified other bit vector - fn union_with(&mut self, other: &BitvSet) { - self.other_op(other, |w1, w2| w1 | w2); - } - - /// Intersect in-place with the specified other bit vector - fn intersect_with(&mut self, other: &BitvSet) { - self.other_op(other, |w1, w2| w1 & w2); - } - - /// Difference in-place with the specified other bit vector - fn difference_with(&mut self, other: &BitvSet) { - self.other_op(other, |w1, w2| w1 & !w2); - } - - /// Symmetric difference in-place with the specified other bit vector - fn symmetric_difference_with(&mut self, other: &BitvSet) { - self.other_op(other, |w1, w2| w1 ^ w2); - } -} - -impl BaseIter<uint> for BitvSet { - fn size_hint(&self) -> Option<uint> { Some(self.len()) } - - fn each(&self, blk: &fn(v: &uint) -> bool) -> bool { - for self.bitv.storage.eachi |i, &w| { - if !iterate_bits(i * uint::bits, w, |b| blk(&b)) { - return false; - } - } - return true; - } -} - -impl cmp::Eq for BitvSet { - fn eq(&self, other: &BitvSet) -> bool { - if self.size != other.size { - return false; - } - for self.each_common(other) |_, w1, w2| { - if w1 != w2 { - return false; - } - } - for self.each_outlier(other) |_, _, w| { - if w != 0 { - return false; - } - } - return true; - } - - fn ne(&self, other: &BitvSet) -> bool { !self.eq(other) } -} - -impl Container for BitvSet { - fn len(&const self) -> uint { self.size } - fn is_empty(&const self) -> bool { self.size == 0 } -} - -impl Mutable for BitvSet { - fn clear(&mut self) { - for self.bitv.each_storage |w| { *w = 0; } - self.size = 0; - } -} - -impl Set<uint> for BitvSet { - fn contains(&self, value: &uint) -> bool { - *value < self.bitv.storage.len() * uint::bits && self.bitv.get(*value) - } - - fn insert(&mut self, value: uint) -> bool { - if self.contains(&value) { - return false; - } - let nbits = self.capacity(); - if value >= nbits { - let newsize = uint::max(value, nbits * 2) / uint::bits + 1; - assert!(newsize > self.bitv.storage.len()); - self.bitv.storage.grow(newsize, &0); - } - self.size += 1; - self.bitv.set(value, true); - return true; - } - - fn remove(&mut self, value: &uint) -> bool { - if !self.contains(value) { - return false; - } - self.size -= 1; - self.bitv.set(*value, false); - - // Attempt to truncate our storage - let mut i = self.bitv.storage.len(); - while i > 1 && self.bitv.storage[i - 1] == 0 { - i -= 1; - } - self.bitv.storage.truncate(i); - - return true; - } - - fn is_disjoint(&self, other: &BitvSet) -> bool { - for self.intersection(other) |_| { - return false; - } - return true; - } - - fn is_subset(&self, other: &BitvSet) -> bool { - for self.each_common(other) |_, w1, w2| { - if w1 & w2 != w1 { - return false; - } - } - /* If anything is not ours, then everything is not ours so we're - definitely a subset in that case. Otherwise if there's any stray - ones that 'other' doesn't have, we're not a subset. */ - for self.each_outlier(other) |mine, _, w| { - if !mine { - return true; - } else if w != 0 { - return false; - } - } - return true; - } - - fn is_superset(&self, other: &BitvSet) -> bool { - other.is_subset(self) - } - - fn difference(&self, other: &BitvSet, f: &fn(&uint) -> bool) -> bool { - for self.each_common(other) |i, w1, w2| { - if !iterate_bits(i, w1 & !w2, |b| f(&b)) { - return false; - } - } - /* everything we have that they don't also shows up */ - self.each_outlier(other, |mine, i, w| - !mine || iterate_bits(i, w, |b| f(&b)) - ) - } - - fn symmetric_difference(&self, other: &BitvSet, - f: &fn(&uint) -> bool) -> bool { - for self.each_common(other) |i, w1, w2| { - if !iterate_bits(i, w1 ^ w2, |b| f(&b)) { - return false; - } - } - self.each_outlier(other, |_, i, w| iterate_bits(i, w, |b| f(&b))) - } - - fn intersection(&self, other: &BitvSet, f: &fn(&uint) -> bool) -> bool { - self.each_common(other, |i, w1, w2| iterate_bits(i, w1 & w2, |b| f(&b))) - } - - fn union(&self, other: &BitvSet, f: &fn(&uint) -> bool) -> bool { - for self.each_common(other) |i, w1, w2| { - if !iterate_bits(i, w1 | w2, |b| f(&b)) { - return false; - } - } - self.each_outlier(other, |_, i, w| iterate_bits(i, w, |b| f(&b))) - } -} - -priv impl BitvSet { - /// Visits each of the words that the two bit vectors (self and other) - /// both have in common. The three yielded arguments are (bit location, - /// w1, w2) where the bit location is the number of bits offset so far, - /// and w1/w2 are the words coming from the two vectors self, other. - fn each_common(&self, other: &BitvSet, - f: &fn(uint, uint, uint) -> bool) -> bool { - let min = uint::min(self.bitv.storage.len(), - other.bitv.storage.len()); - self.bitv.storage.slice(0, min).eachi(|i, &w| { - f(i * uint::bits, w, other.bitv.storage[i]) - }) - } - - /// Visits each word in self or other that extends beyond the other. This - /// will only iterate through one of the vectors, and it only iterates - /// over the portion that doesn't overlap with the other one. - /// - /// The yielded arguments are a bool, the bit offset, and a word. The bool - /// is true if the word comes from 'self', and false if it comes from - /// 'other'. - fn each_outlier(&self, other: &BitvSet, - f: &fn(bool, uint, uint) -> bool) -> bool { - let len1 = self.bitv.storage.len(); - let len2 = other.bitv.storage.len(); - let min = uint::min(len1, len2); - - /* only one of these loops will execute and that's the point */ - for self.bitv.storage.slice(min, len1).eachi |i, &w| { - if !f(true, (i + min) * uint::bits, w) { - return false; - } - } - for other.bitv.storage.slice(min, len2).eachi |i, &w| { - if !f(false, (i + min) * uint::bits, w) { - return false; - } - } - return true; - } -} - -#[cfg(test)] -mod tests { - use std::test::BenchHarness; - - use bitv::*; - use bitv; - - use core::uint; - use core::vec; - use core::rand; - use core::rand::Rng; - - static bench_bits : uint = 1 << 14; - - #[test] - fn test_to_str() { - let zerolen = Bitv::new(0u, false); - assert_eq!(zerolen.to_str(), ~""); - - let eightbits = Bitv::new(8u, false); - assert_eq!(eightbits.to_str(), ~"00000000"); - } - - #[test] - fn test_0_elements() { - let mut act; - let exp; - act = Bitv::new(0u, false); - exp = vec::from_elem::<uint>(0u, 0u); - assert!(act.eq_vec(exp)); - } - - #[test] - fn test_1_element() { - let mut act; - act = Bitv::new(1u, false); - assert!(act.eq_vec(~[0u])); - act = Bitv::new(1u, true); - assert!(act.eq_vec(~[1u])); - } - - #[test] - fn test_2_elements() { - let mut b = bitv::Bitv::new(2, false); - b.set(0, true); - b.set(1, false); - assert_eq!(b.to_str(), ~"10"); - } - - #[test] - fn test_10_elements() { - let mut act; - // all 0 - - act = Bitv::new(10u, false); - assert!((act.eq_vec(~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u]))); - // all 1 - - act = Bitv::new(10u, true); - assert!((act.eq_vec(~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u]))); - // mixed - - act = Bitv::new(10u, false); - act.set(0u, true); - act.set(1u, true); - act.set(2u, true); - act.set(3u, true); - act.set(4u, true); - assert!((act.eq_vec(~[1u, 1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u, 0u]))); - // mixed - - act = Bitv::new(10u, false); - act.set(5u, true); - act.set(6u, true); - act.set(7u, true); - act.set(8u, true); - act.set(9u, true); - assert!((act.eq_vec(~[0u, 0u, 0u, 0u, 0u, 1u, 1u, 1u, 1u, 1u]))); - // mixed - - act = Bitv::new(10u, false); - act.set(0u, true); - act.set(3u, true); - act.set(6u, true); - act.set(9u, true); - assert!((act.eq_vec(~[1u, 0u, 0u, 1u, 0u, 0u, 1u, 0u, 0u, 1u]))); - } - - #[test] - fn test_31_elements() { - let mut act; - // all 0 - - act = Bitv::new(31u, false); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u])); - // all 1 - - act = Bitv::new(31u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u])); - // mixed - - act = Bitv::new(31u, false); - act.set(0u, true); - act.set(1u, true); - act.set(2u, true); - act.set(3u, true); - act.set(4u, true); - act.set(5u, true); - act.set(6u, true); - act.set(7u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(31u, false); - act.set(16u, true); - act.set(17u, true); - act.set(18u, true); - act.set(19u, true); - act.set(20u, true); - act.set(21u, true); - act.set(22u, true); - act.set(23u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(31u, false); - act.set(24u, true); - act.set(25u, true); - act.set(26u, true); - act.set(27u, true); - act.set(28u, true); - act.set(29u, true); - act.set(30u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u])); - // mixed - - act = Bitv::new(31u, false); - act.set(3u, true); - act.set(17u, true); - act.set(30u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u])); - } - - #[test] - fn test_32_elements() { - let mut act; - // all 0 - - act = Bitv::new(32u, false); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u])); - // all 1 - - act = Bitv::new(32u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u])); - // mixed - - act = Bitv::new(32u, false); - act.set(0u, true); - act.set(1u, true); - act.set(2u, true); - act.set(3u, true); - act.set(4u, true); - act.set(5u, true); - act.set(6u, true); - act.set(7u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(32u, false); - act.set(16u, true); - act.set(17u, true); - act.set(18u, true); - act.set(19u, true); - act.set(20u, true); - act.set(21u, true); - act.set(22u, true); - act.set(23u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(32u, false); - act.set(24u, true); - act.set(25u, true); - act.set(26u, true); - act.set(27u, true); - act.set(28u, true); - act.set(29u, true); - act.set(30u, true); - act.set(31u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u])); - // mixed - - act = Bitv::new(32u, false); - act.set(3u, true); - act.set(17u, true); - act.set(30u, true); - act.set(31u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u, 1u])); - } - - #[test] - fn test_33_elements() { - let mut act; - // all 0 - - act = Bitv::new(33u, false); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u])); - // all 1 - - act = Bitv::new(33u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u, 1u])); - // mixed - - act = Bitv::new(33u, false); - act.set(0u, true); - act.set(1u, true); - act.set(2u, true); - act.set(3u, true); - act.set(4u, true); - act.set(5u, true); - act.set(6u, true); - act.set(7u, true); - assert!(act.eq_vec( - ~[1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(33u, false); - act.set(16u, true); - act.set(17u, true); - act.set(18u, true); - act.set(19u, true); - act.set(20u, true); - act.set(21u, true); - act.set(22u, true); - act.set(23u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u])); - // mixed - - act = Bitv::new(33u, false); - act.set(24u, true); - act.set(25u, true); - act.set(26u, true); - act.set(27u, true); - act.set(28u, true); - act.set(29u, true); - act.set(30u, true); - act.set(31u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u, - 1u, 1u, 1u, 1u, 1u, 1u, 0u])); - // mixed - - act = Bitv::new(33u, false); - act.set(3u, true); - act.set(17u, true); - act.set(30u, true); - act.set(31u, true); - act.set(32u, true); - assert!(act.eq_vec( - ~[0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, - 0u, 0u, 0u, 0u, 1u, 1u, 1u])); - } - - #[test] - fn test_equal_differing_sizes() { - let v0 = Bitv::new(10u, false); - let v1 = Bitv::new(11u, false); - assert!(!v0.equal(&v1)); - } - - #[test] - fn test_equal_greatly_differing_sizes() { - let v0 = Bitv::new(10u, false); - let v1 = Bitv::new(110u, false); - assert!(!v0.equal(&v1)); - } - - #[test] - fn test_equal_sneaky_small() { - let mut a = bitv::Bitv::new(1, false); - a.set(0, true); - - let mut b = bitv::Bitv::new(1, true); - b.set(0, true); - - assert!(a.equal(&b)); - } - - #[test] - fn test_equal_sneaky_big() { - let mut a = bitv::Bitv::new(100, false); - for uint::range(0, 100) |i| { - a.set(i, true); - } - - let mut b = bitv::Bitv::new(100, true); - for uint::range(0, 100) |i| { - b.set(i, true); - } - - assert!(a.equal(&b)); - } - - #[test] - fn test_from_bytes() { - let bitv = from_bytes([0b10110110, 0b00000000, 0b11111111]); - let str = ~"10110110" + ~"00000000" + ~"11111111"; - assert_eq!(bitv.to_str(), str); - } - - #[test] - fn test_to_bytes() { - let mut bv = Bitv::new(3, true); - bv.set(1, false); - assert_eq!(bv.to_bytes(), ~[0b10100000]); - - let mut bv = Bitv::new(9, false); - bv.set(2, true); - bv.set(8, true); - assert_eq!(bv.to_bytes(), ~[0b00100000, 0b10000000]); - } - - #[test] - fn test_from_bools() { - assert!(from_bools([true, false, true, true]).to_str() == - ~"1011"); - } - - #[test] - fn test_to_bools() { - let bools = ~[false, false, true, false, false, true, true, false]; - assert_eq!(from_bytes([0b00100110]).to_bools(), bools); - } - - #[test] - fn test_small_difference() { - let mut b1 = Bitv::new(3, false); - let mut b2 = Bitv::new(3, false); - b1.set(0, true); - b1.set(1, true); - b2.set(1, true); - b2.set(2, true); - assert!(b1.difference(&b2)); - assert!(b1[0]); - assert!(!b1[1]); - assert!(!b1[2]); - } - - #[test] - fn test_big_difference() { - let mut b1 = Bitv::new(100, false); - let mut b2 = Bitv::new(100, false); - b1.set(0, true); - b1.set(40, true); - b2.set(40, true); - b2.set(80, true); - assert!(b1.difference(&b2)); - assert!(b1[0]); - assert!(!b1[40]); - assert!(!b1[80]); - } - - #[test] - fn test_small_clear() { - let mut b = Bitv::new(14, true); - b.clear(); - for b.ones |i| { - fail!("found 1 at %?", i); - } - } - - #[test] - fn test_big_clear() { - let mut b = Bitv::new(140, true); - b.clear(); - for b.ones |i| { - fail!("found 1 at %?", i); - } - } - - #[test] - fn test_bitv_set_basic() { - let mut b = BitvSet::new(); - assert!(b.insert(3)); - assert!(!b.insert(3)); - assert!(b.contains(&3)); - assert!(b.insert(400)); - assert!(!b.insert(400)); - assert!(b.contains(&400)); - assert_eq!(b.len(), 2); - } - - #[test] - fn test_bitv_set_intersection() { - let mut a = BitvSet::new(); - let mut b = BitvSet::new(); - - assert!(a.insert(11)); - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(77)); - assert!(a.insert(103)); - assert!(a.insert(5)); - - assert!(b.insert(2)); - assert!(b.insert(11)); - assert!(b.insert(77)); - assert!(b.insert(5)); - assert!(b.insert(3)); - - let mut i = 0; - let expected = [3, 5, 11, 77]; - for a.intersection(&b) |x| { - assert_eq!(*x, expected[i]); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_bitv_set_difference() { - let mut a = BitvSet::new(); - let mut b = BitvSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(200)); - assert!(a.insert(500)); - - assert!(b.insert(3)); - assert!(b.insert(200)); - - let mut i = 0; - let expected = [1, 5, 500]; - for a.difference(&b) |x| { - assert_eq!(*x, expected[i]); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_bitv_set_symmetric_difference() { - let mut a = BitvSet::new(); - let mut b = BitvSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(3)); - assert!(b.insert(9)); - assert!(b.insert(14)); - assert!(b.insert(220)); - - let mut i = 0; - let expected = [1, 5, 11, 14, 220]; - for a.symmetric_difference(&b) |x| { - assert_eq!(*x, expected[i]); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_bitv_set_union() { - let mut a = BitvSet::new(); - let mut b = BitvSet::new(); - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - assert!(a.insert(160)); - assert!(a.insert(19)); - assert!(a.insert(24)); - - assert!(b.insert(1)); - assert!(b.insert(5)); - assert!(b.insert(9)); - assert!(b.insert(13)); - assert!(b.insert(19)); - - let mut i = 0; - let expected = [1, 3, 5, 9, 11, 13, 19, 24, 160]; - for a.union(&b) |x| { - assert_eq!(*x, expected[i]); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_bitv_remove() { - let mut a = BitvSet::new(); - - assert!(a.insert(1)); - assert!(a.remove(&1)); - - assert!(a.insert(100)); - assert!(a.remove(&100)); - - assert!(a.insert(1000)); - assert!(a.remove(&1000)); - assert_eq!(a.capacity(), uint::bits); - } - - fn rng() -> rand::IsaacRng { - let seed = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; - rand::IsaacRng::new_seeded(seed) - } - - #[bench] - fn bench_uint_small(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = 0 as uint; - do b.iter { - bitv |= (1 << ((r.next() as uint) % uint::bits)); - } - } - - #[bench] - fn bench_small_bitv_small(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = SmallBitv::new(uint::bits); - do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); - } - } - - #[bench] - fn bench_big_bitv_small(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = BigBitv::new(~[0]); - do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); - } - } - - #[bench] - fn bench_big_bitv_big(b: &mut BenchHarness) { - let mut r = rng(); - let mut storage = ~[]; - storage.grow(bench_bits / uint::bits, &0); - let mut bitv = BigBitv::new(storage); - do b.iter { - bitv.set((r.next() as uint) % bench_bits, true); - } - } - - #[bench] - fn bench_bitv_big(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = Bitv::new(bench_bits, false); - do b.iter { - bitv.set((r.next() as uint) % bench_bits, true); - } - } - - #[bench] - fn bench_bitv_small(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = Bitv::new(uint::bits, false); - do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); - } - } - - #[bench] - fn bench_bitv_set_small(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = BitvSet::new(); - do b.iter { - bitv.insert((r.next() as uint) % uint::bits); - } - } - - #[bench] - fn bench_bitv_set_big(b: &mut BenchHarness) { - let mut r = rng(); - let mut bitv = BitvSet::new(); - do b.iter { - bitv.insert((r.next() as uint) % bench_bits); - } - } - - #[bench] - fn bench_bitv_big_union(b: &mut BenchHarness) { - let mut b1 = Bitv::new(bench_bits, false); - let mut b2 = Bitv::new(bench_bits, false); - do b.iter { - b1.union(&b2); - } - } -} diff --git a/src/libstd/bool.rs b/src/libstd/bool.rs new file mode 100644 index 00000000000..5ab7f838a57 --- /dev/null +++ b/src/libstd/bool.rs @@ -0,0 +1,153 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Boolean logic + +#[cfg(not(test))] +use cmp::{Eq, Ord, TotalOrd, Ordering}; +use option::{None, Option, Some}; +use from_str::FromStr; + +/// Negation / inverse +pub fn not(v: bool) -> bool { !v } + +/// Conjunction +pub fn and(a: bool, b: bool) -> bool { a && b } + +/// Disjunction +pub fn or(a: bool, b: bool) -> bool { a || b } + +/** + * Exclusive or + * + * Identical to `or(and(a, not(b)), and(not(a), b))` + */ +pub fn xor(a: bool, b: bool) -> bool { (a && !b) || (!a && b) } + +/// Implication in the logic, i.e. from `a` follows `b` +pub fn implies(a: bool, b: bool) -> bool { !a || b } + +/// true if truth values `a` and `b` are indistinguishable in the logic +pub fn eq(a: bool, b: bool) -> bool { a == b } + +/// true if truth values `a` and `b` are distinguishable in the logic +pub fn ne(a: bool, b: bool) -> bool { a != b } + +/// true if `v` represents truth in the logic +pub fn is_true(v: bool) -> bool { v } + +/// true if `v` represents falsehood in the logic +pub fn is_false(v: bool) -> bool { !v } + +/// Parse logic value from `s` +impl FromStr for bool { + fn from_str(s: &str) -> Option<bool> { + match s { + "true" => Some(true), + "false" => Some(false), + _ => None, + } + } +} + +/// Convert `v` into a string +pub fn to_str(v: bool) -> ~str { if v { ~"true" } else { ~"false" } } + +/** + * Iterates over all truth values by passing them to `blk` in an unspecified + * order + */ +pub fn all_values(blk: &fn(v: bool)) { + blk(true); + blk(false); +} + +/// converts truth value to an 8 bit byte +#[inline(always)] +pub fn to_bit(v: bool) -> u8 { if v { 1u8 } else { 0u8 } } + +#[cfg(not(test))] +impl Ord for bool { + #[inline(always)] + fn lt(&self, other: &bool) -> bool { to_bit(*self) < to_bit(*other) } + #[inline(always)] + fn le(&self, other: &bool) -> bool { to_bit(*self) <= to_bit(*other) } + #[inline(always)] + fn gt(&self, other: &bool) -> bool { to_bit(*self) > to_bit(*other) } + #[inline(always)] + fn ge(&self, other: &bool) -> bool { to_bit(*self) >= to_bit(*other) } +} + +#[cfg(not(test))] +impl TotalOrd for bool { + #[inline(always)] + fn cmp(&self, other: &bool) -> Ordering { to_bit(*self).cmp(&to_bit(*other)) } +} + +#[cfg(not(test))] +impl Eq for bool { + #[inline(always)] + fn eq(&self, other: &bool) -> bool { (*self) == (*other) } + #[inline(always)] + fn ne(&self, other: &bool) -> bool { (*self) != (*other) } +} + +#[cfg(test)] +mod tests { + use super::*; + use prelude::*; + + #[test] + fn test_bool_from_str() { + do all_values |v| { + assert!(Some(v) == FromStr::from_str(to_str(v))) + } + } + + #[test] + fn test_bool_to_str() { + assert_eq!(to_str(false), ~"false"); + assert_eq!(to_str(true), ~"true"); + } + + #[test] + fn test_bool_to_bit() { + do all_values |v| { + assert_eq!(to_bit(v), if is_true(v) { 1u8 } else { 0u8 }); + } + } + + #[test] + fn test_bool_ord() { + assert!(true > false); + assert!(!(false > true)); + + assert!(false < true); + assert!(!(true < false)); + + assert!(false <= false); + assert!(false >= false); + assert!(true <= true); + assert!(true >= true); + + assert!(false <= true); + assert!(!(false >= true)); + assert!(true >= false); + assert!(!(true <= false)); + } + + #[test] + fn test_bool_totalord() { + assert_eq!(true.cmp(&true), Equal); + assert_eq!(false.cmp(&false), Equal); + assert_eq!(true.cmp(&false), Greater); + assert_eq!(false.cmp(&true), Less); + } +} diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs deleted file mode 100644 index e463e597188..00000000000 --- a/src/libstd/c_vec.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * Library to interface with chunks of memory allocated in C. - * - * It is often desirable to safely interface with memory allocated from C, - * encapsulating the unsafety into allocation and destruction time. Indeed, - * allocating memory externally is currently the only way to give Rust shared - * mut state with C programs that keep their own references; vectors are - * unsuitable because they could be reallocated or moved at any time, and - * importing C memory into a vector takes a one-time snapshot of the memory. - * - * This module simplifies the usage of such external blocks of memory. Memory - * is encapsulated into an opaque object after creation; the lifecycle of the - * memory can be optionally managed by Rust, if an appropriate destructor - * closure is provided. Safety is ensured by bounds-checking accesses, which - * are marshalled through get and set functions. - * - * There are three unsafe functions: the two introduction forms, and the - * pointer elimination form. The introduction forms are unsafe for the - * obvious reason (they act on a pointer that cannot be checked inside the - * method), but the elimination form is somewhat more subtle in its unsafety. - * By using a pointer taken from a c_vec::t without keeping a reference to the - * c_vec::t itself around, the CVec could be garbage collected, and the - * memory within could be destroyed. There are legitimate uses for the - * pointer elimination form -- for instance, to pass memory back into C -- but - * great care must be taken to ensure that a reference to the c_vec::t is - * still held if needed. - */ - -/** - * The type representing a foreign chunk of memory - * - */ -pub struct CVec<T> { - priv base: *mut T, - priv len: uint, - priv rsrc: @DtorRes -} - -struct DtorRes { - dtor: Option<@fn()>, -} - -#[unsafe_destructor] -impl Drop for DtorRes { - fn finalize(&self) { - match self.dtor { - option::None => (), - option::Some(f) => f() - } - } -} - -fn DtorRes(dtor: Option<@fn()>) -> DtorRes { - DtorRes { - dtor: dtor - } -} - -/* - Section: Introduction forms - */ - -/** - * Create a `CVec` from a foreign buffer with a given length. - * - * # Arguments - * - * * base - A foreign pointer to a buffer - * * len - The number of elements in the buffer - */ -pub unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> { - return CVec{ - base: base, - len: len, - rsrc: @DtorRes(option::None) - }; -} - -/** - * Create a `CVec` from a foreign buffer, with a given length, - * and a function to run upon destruction. - * - * # Arguments - * - * * base - A foreign pointer to a buffer - * * len - The number of elements in the buffer - * * dtor - A function to run when the value is destructed, useful - * for freeing the buffer, etc. - */ -pub unsafe fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: @fn()) - -> CVec<T> { - return CVec{ - base: base, - len: len, - rsrc: @DtorRes(option::Some(dtor)) - }; -} - -/* - Section: Operations - */ - -/** - * Retrieves an element at a given index - * - * Fails if `ofs` is greater or equal to the length of the vector - */ -pub fn get<T:Copy>(t: CVec<T>, ofs: uint) -> T { - assert!(ofs < len(t)); - return unsafe { *ptr::mut_offset(t.base, ofs) }; -} - -/** - * Sets the value of an element at a given index - * - * Fails if `ofs` is greater or equal to the length of the vector - */ -pub fn set<T:Copy>(t: CVec<T>, ofs: uint, v: T) { - assert!(ofs < len(t)); - unsafe { *ptr::mut_offset(t.base, ofs) = v }; -} - -/* - Section: Elimination forms - */ - -/// Returns the length of the vector -pub fn len<T>(t: CVec<T>) -> uint { t.len } - -/// Returns a pointer to the first element of the vector -pub unsafe fn ptr<T>(t: CVec<T>) -> *mut T { t.base } - -#[cfg(test)] -mod tests { - - use c_vec::*; - - use core::libc::*; - use core::libc; - - fn malloc(n: size_t) -> CVec<u8> { - unsafe { - let mem = libc::malloc(n); - - assert!(mem as int != 0); - - return c_vec_with_dtor(mem as *mut u8, n as uint, - || unsafe { free(mem) }); - } - } - - #[test] - fn test_basic() { - let cv = malloc(16u as size_t); - - set(cv, 3u, 8u8); - set(cv, 4u, 9u8); - assert_eq!(get(cv, 3u), 8u8); - assert_eq!(get(cv, 4u), 9u8); - assert_eq!(len(cv), 16u); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_overrun_get() { - let cv = malloc(16u as size_t); - - get(cv, 17u); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_overrun_set() { - let cv = malloc(16u as size_t); - - set(cv, 17u, 0u8); - } - - #[test] - fn test_and_I_mean_it() { - let cv = malloc(16u as size_t); - let p = unsafe { ptr(cv) }; - - set(cv, 0u, 32u8); - set(cv, 1u, 33u8); - assert_eq!(unsafe { *p }, 32u8); - set(cv, 2u, 34u8); /* safety */ - } - -} diff --git a/src/libstd/cast.rs b/src/libstd/cast.rs new file mode 100644 index 00000000000..e3336e24a6e --- /dev/null +++ b/src/libstd/cast.rs @@ -0,0 +1,160 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Unsafe casting functions + +use sys; +use unstable; +use unstable::intrinsics; + +/// Casts the value at `src` to U. The two types must have the same length. +pub unsafe fn transmute_copy<T, U>(src: &T) -> U { + let mut dest: U = intrinsics::init(); + { + let dest_ptr: *mut u8 = transmute(&mut dest); + let src_ptr: *u8 = transmute(src); + intrinsics::memmove64(dest_ptr, + src_ptr, + sys::size_of::<U>() as u64); + } + dest +} + +/** + * Move a thing into the void + * + * The forget function will take ownership of the provided value but neglect + * to run any required cleanup or memory-management operations on it. This + * can be used for various acts of magick, particularly when using + * reinterpret_cast on pointer types. + */ +#[inline(always)] +pub unsafe fn forget<T>(thing: T) { intrinsics::forget(thing); } + +/** + * Force-increment the reference count on a shared box. If used + * carelessly, this can leak the box. Use this in conjunction with transmute + * and/or reinterpret_cast when such calls would otherwise scramble a box's + * reference count + */ +pub unsafe fn bump_box_refcount<T>(t: @T) { forget(t); } + +/** + * Transform a value of one type into a value of another type. + * Both types must have the same size and alignment. + * + * # Example + * + * assert!(transmute("L") == ~[76u8, 0u8]); + */ +#[inline(always)] +pub unsafe fn transmute<L, G>(thing: L) -> G { + intrinsics::transmute(thing) +} + +/// Coerce an immutable reference to be mutable. +#[inline(always)] +pub unsafe fn transmute_mut<'a,T>(ptr: &'a T) -> &'a mut T { transmute(ptr) } + +/// Coerce a mutable reference to be immutable. +#[inline(always)] +pub unsafe fn transmute_immut<'a,T>(ptr: &'a mut T) -> &'a T { + transmute(ptr) +} + +/// Coerce a borrowed pointer to have an arbitrary associated region. +#[inline(always)] +pub unsafe fn transmute_region<'a,'b,T>(ptr: &'a T) -> &'b T { + transmute(ptr) +} + +/// Coerce an immutable reference to be mutable. +#[inline(always)] +pub unsafe fn transmute_mut_unsafe<T>(ptr: *const T) -> *mut T { + transmute(ptr) +} + +/// Coerce an immutable reference to be mutable. +#[inline(always)] +pub unsafe fn transmute_immut_unsafe<T>(ptr: *const T) -> *T { + transmute(ptr) +} + +/// Coerce a borrowed mutable pointer to have an arbitrary associated region. +#[inline(always)] +pub unsafe fn transmute_mut_region<'a,'b,T>(ptr: &'a mut T) -> &'b mut T { + transmute(ptr) +} + +/// Transforms lifetime of the second pointer to match the first. +#[inline(always)] +pub unsafe fn copy_lifetime<'a,S,T>(_ptr: &'a S, ptr: &T) -> &'a T { + transmute_region(ptr) +} + +/// Transforms lifetime of the second pointer to match the first. +#[inline(always)] +pub unsafe fn copy_mut_lifetime<'a,S,T>(_ptr: &'a mut S, ptr: &mut T) -> &'a mut T { + transmute_mut_region(ptr) +} + +/// Transforms lifetime of the second pointer to match the first. +#[inline(always)] +pub unsafe fn copy_lifetime_vec<'a,S,T>(_ptr: &'a [S], ptr: &T) -> &'a T { + transmute_region(ptr) +} + + +/**************************************************************************** + * Tests + ****************************************************************************/ + +#[cfg(test)] +mod tests { + use cast::{bump_box_refcount, transmute}; + + #[test] + fn test_transmute_copy() { + assert_eq!(1u, unsafe { ::cast::transmute_copy(&1) }); + } + + #[test] + fn test_bump_box_refcount() { + unsafe { + let box = @~"box box box"; // refcount 1 + bump_box_refcount(box); // refcount 2 + let ptr: *int = transmute(box); // refcount 2 + let _box1: @~str = ::cast::transmute_copy(&ptr); + let _box2: @~str = ::cast::transmute_copy(&ptr); + assert!(*_box1 == ~"box box box"); + assert!(*_box2 == ~"box box box"); + // Will destroy _box1 and _box2. Without the bump, this would + // use-after-free. With too many bumps, it would leak. + } + } + + #[test] + fn test_transmute() { + use managed::raw::BoxRepr; + unsafe { + let x = @100u8; + let x: *BoxRepr = transmute(x); + assert!((*x).data == 100); + let _x: @int = transmute(x); + } + } + + #[test] + fn test_transmute2() { + unsafe { + assert_eq!(~[76u8, 0u8], transmute(~"L")); + } + } +} diff --git a/src/libstd/cell.rs b/src/libstd/cell.rs new file mode 100644 index 00000000000..b707e3bbb9e --- /dev/null +++ b/src/libstd/cell.rs @@ -0,0 +1,136 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! A mutable, nullable memory location + +use cast::transmute_mut; +use prelude::*; +use util::replace; + +/* +A dynamic, mutable location. + +Similar to a mutable option type, but friendlier. +*/ + +#[mutable] +#[deriving(Clone)] +pub struct Cell<T> { + priv value: Option<T> +} + +impl<T: DeepClone> DeepClone for Cell<T> { + fn deep_clone(&self) -> Cell<T> { + Cell{value: self.value.deep_clone()} + } +} + +impl<T:cmp::Eq> cmp::Eq for Cell<T> { + fn eq(&self, other: &Cell<T>) -> bool { + (self.value) == (other.value) + } + fn ne(&self, other: &Cell<T>) -> bool { !self.eq(other) } +} + +/// Creates a new full cell with the given value. +pub fn Cell<T>(value: T) -> Cell<T> { + Cell { value: Some(value) } +} + +pub fn empty_cell<T>() -> Cell<T> { + Cell { value: None } +} + +pub impl<T> Cell<T> { + /// Yields the value, failing if the cell is empty. + fn take(&self) -> T { + let this = unsafe { transmute_mut(self) }; + if this.is_empty() { + fail!("attempt to take an empty cell"); + } + + replace(&mut this.value, None).unwrap() + } + + /// Returns the value, failing if the cell is full. + fn put_back(&self, value: T) { + let this = unsafe { transmute_mut(self) }; + if !this.is_empty() { + fail!("attempt to put a value back into a full cell"); + } + this.value = Some(value); + } + + /// Returns true if the cell is empty and false if the cell is full. + fn is_empty(&self) -> bool { + self.value.is_none() + } + + // Calls a closure with a reference to the value. + fn with_ref<R>(&self, op: &fn(v: &T) -> R) -> R { + let v = self.take(); + let r = op(&v); + self.put_back(v); + r + } + + // Calls a closure with a mutable reference to the value. + fn with_mut_ref<R>(&self, op: &fn(v: &mut T) -> R) -> R { + let mut v = self.take(); + let r = op(&mut v); + self.put_back(v); + r + } +} + +#[test] +fn test_basic() { + let value_cell = Cell(~10); + assert!(!value_cell.is_empty()); + let value = value_cell.take(); + assert!(value == ~10); + assert!(value_cell.is_empty()); + value_cell.put_back(value); + assert!(!value_cell.is_empty()); +} + +#[test] +#[should_fail] +#[ignore(cfg(windows))] +fn test_take_empty() { + let value_cell = empty_cell::<~int>(); + value_cell.take(); +} + +#[test] +#[should_fail] +#[ignore(cfg(windows))] +fn test_put_back_non_empty() { + let value_cell = Cell(~10); + value_cell.put_back(~20); +} + +#[test] +fn test_with_ref() { + let good = 6; + let c = Cell(~[1, 2, 3, 4, 5, 6]); + let l = do c.with_ref() |v| { v.len() }; + assert_eq!(l, good); +} + +#[test] +fn test_with_mut_ref() { + let good = ~[1, 2, 3]; + let v = ~[1, 2]; + let c = Cell(v); + do c.with_mut_ref() |v| { v.push(3); } + let v = c.take(); + assert_eq!(v, good); +} diff --git a/src/libstd/char.rs b/src/libstd/char.rs new file mode 100644 index 00000000000..bd70f59212d --- /dev/null +++ b/src/libstd/char.rs @@ -0,0 +1,406 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Utilities for manipulating the char type + +use option::{None, Option, Some}; +use str; +use str::{StrSlice, OwnedStr}; +use u32; +use uint; +use unicode::{derived_property, general_category}; + +#[cfg(not(test))] +use cmp::{Eq, Ord}; + +/* + Lu Uppercase_Letter an uppercase letter + Ll Lowercase_Letter a lowercase letter + Lt Titlecase_Letter a digraphic character, with first part uppercase + Lm Modifier_Letter a modifier letter + Lo Other_Letter other letters, including syllables and ideographs + Mn Nonspacing_Mark a nonspacing combining mark (zero advance width) + Mc Spacing_Mark a spacing combining mark (positive advance width) + Me Enclosing_Mark an enclosing combining mark + Nd Decimal_Number a decimal digit + Nl Letter_Number a letterlike numeric character + No Other_Number a numeric character of other type + Pc Connector_Punctuation a connecting punctuation mark, like a tie + Pd Dash_Punctuation a dash or hyphen punctuation mark + Ps Open_Punctuation an opening punctuation mark (of a pair) + Pe Close_Punctuation a closing punctuation mark (of a pair) + Pi Initial_Punctuation an initial quotation mark + Pf Final_Punctuation a final quotation mark + Po Other_Punctuation a punctuation mark of other type + Sm Math_Symbol a symbol of primarily mathematical use + Sc Currency_Symbol a currency sign + Sk Modifier_Symbol a non-letterlike modifier symbol + So Other_Symbol a symbol of other type + Zs Space_Separator a space character (of various non-zero widths) + Zl Line_Separator U+2028 LINE SEPARATOR only + Zp Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only + Cc Control a C0 or C1 control code + Cf Format a format control character + Cs Surrogate a surrogate code point + Co Private_Use a private-use character + Cn Unassigned a reserved unassigned code point or a noncharacter +*/ + +pub fn is_alphabetic(c: char) -> bool { derived_property::Alphabetic(c) } +pub fn is_XID_start(c: char) -> bool { derived_property::XID_Start(c) } +pub fn is_XID_continue(c: char) -> bool { derived_property::XID_Continue(c) } + +/// +/// Indicates whether a character is in lower case, defined +/// in terms of the Unicode General Category 'Ll' +/// +#[inline(always)] +pub fn is_lowercase(c: char) -> bool { general_category::Ll(c) } + +/// +/// Indicates whether a character is in upper case, defined +/// in terms of the Unicode General Category 'Lu'. +/// +#[inline(always)] +pub fn is_uppercase(c: char) -> bool { general_category::Lu(c) } + +/// +/// Indicates whether a character is whitespace. Whitespace is defined in +/// terms of the Unicode General Categories 'Zs', 'Zl', 'Zp' +/// additional 'Cc'-category control codes in the range [0x09, 0x0d] +/// +#[inline(always)] +pub fn is_whitespace(c: char) -> bool { + ('\x09' <= c && c <= '\x0d') + || general_category::Zs(c) + || general_category::Zl(c) + || general_category::Zp(c) +} + +/// +/// Indicates whether a character is alphanumeric. Alphanumericness is +/// defined in terms of the Unicode General Categories 'Nd', 'Nl', 'No' +/// and the Derived Core Property 'Alphabetic'. +/// +#[inline(always)] +pub fn is_alphanumeric(c: char) -> bool { + derived_property::Alphabetic(c) + || general_category::Nd(c) + || general_category::Nl(c) + || general_category::No(c) +} + +/// Indicates whether the character is numeric (Nd, Nl, or No) +#[inline(always)] +pub fn is_digit(c: char) -> bool { + general_category::Nd(c) + || general_category::Nl(c) + || general_category::No(c) +} + +/// +/// Checks if a character parses as a numeric digit in the given radix. +/// Compared to `is_digit()`, this function only recognizes the +/// characters `0-9`, `a-z` and `A-Z`. +/// +/// # Return value +/// +/// Returns `true` if `c` is a valid digit under `radix`, and `false` +/// otherwise. +/// +/// # Failure +/// +/// Fails if given a `radix` > 36. +/// +/// # Note +/// +/// This just wraps `to_digit()`. +/// +#[inline(always)] +pub fn is_digit_radix(c: char, radix: uint) -> bool { + match to_digit(c, radix) { + Some(_) => true, + None => false, + } +} + +/// +/// Convert a char to the corresponding digit. +/// +/// # Return value +/// +/// If `c` is between '0' and '9', the corresponding value +/// between 0 and 9. If `c` is 'a' or 'A', 10. If `c` is +/// 'b' or 'B', 11, etc. Returns none if the char does not +/// refer to a digit in the given radix. +/// +/// # Failure +/// +/// Fails if given a `radix` outside the range `[0..36]`. +/// +#[inline] +pub fn to_digit(c: char, radix: uint) -> Option<uint> { + if radix > 36 { + fail!("to_digit: radix %? is to high (maximum 36)", radix); + } + let val = match c { + '0' .. '9' => c as uint - ('0' as uint), + 'a' .. 'z' => c as uint + 10u - ('a' as uint), + 'A' .. 'Z' => c as uint + 10u - ('A' as uint), + _ => return None, + }; + if val < radix { Some(val) } + else { None } +} + +/// +/// Converts a number to the character representing it. +/// +/// # Return value +/// +/// Returns `Some(char)` if `num` represents one digit under `radix`, +/// using one character of `0-9` or `a-z`, or `None` if it doesn't. +/// +/// # Failure +/// +/// Fails if given an `radix` > 36. +/// +#[inline] +pub fn from_digit(num: uint, radix: uint) -> Option<char> { + if radix > 36 { + fail!("from_digit: radix %? is to high (maximum 36)", num); + } + if num < radix { + if num < 10 { + Some(('0' as uint + num) as char) + } else { + Some(('a' as uint + num - 10u) as char) + } + } else { + None + } +} + +/// +/// Return the hexadecimal unicode escape of a char. +/// +/// The rules are as follows: +/// +/// - chars in [0,0xff] get 2-digit escapes: `\\xNN` +/// - chars in [0x100,0xffff] get 4-digit escapes: `\\uNNNN` +/// - chars above 0x10000 get 8-digit escapes: `\\UNNNNNNNN` +/// +pub fn escape_unicode(c: char) -> ~str { + let s = u32::to_str_radix(c as u32, 16u); + let (c, pad) = cond!( + (c <= '\xff') { ('x', 2u) } + (c <= '\uffff') { ('u', 4u) } + _ { ('U', 8u) } + ); + assert!(s.len() <= pad); + let mut out = ~"\\"; + out.push_str(str::from_char(c)); + for uint::range(s.len(), pad) |_| { + out.push_str("0"); + } + out.push_str(s); + out +} + +/// +/// Return a 'default' ASCII and C++11-like char-literal escape of a char. +/// +/// The default is chosen with a bias toward producing literals that are +/// legal in a variety of languages, including C++11 and similar C-family +/// languages. The exact rules are: +/// +/// - Tab, CR and LF are escaped as '\t', '\r' and '\n' respectively. +/// - Single-quote, double-quote and backslash chars are backslash-escaped. +/// - Any other chars in the range [0x20,0x7e] are not escaped. +/// - Any other chars are given hex unicode escapes; see `escape_unicode`. +/// +pub fn escape_default(c: char) -> ~str { + match c { + '\t' => ~"\\t", + '\r' => ~"\\r", + '\n' => ~"\\n", + '\\' => ~"\\\\", + '\'' => ~"\\'", + '"' => ~"\\\"", + '\x20' .. '\x7e' => str::from_char(c), + _ => c.escape_unicode(), + } +} + +/// Returns the amount of bytes this character would need if encoded in utf8 +pub fn len_utf8_bytes(c: char) -> uint { + static MAX_ONE_B: uint = 128u; + static MAX_TWO_B: uint = 2048u; + static MAX_THREE_B: uint = 65536u; + static MAX_FOUR_B: uint = 2097152u; + + let code = c as uint; + cond!( + (code < MAX_ONE_B) { 1u } + (code < MAX_TWO_B) { 2u } + (code < MAX_THREE_B) { 3u } + (code < MAX_FOUR_B) { 4u } + _ { fail!("invalid character!") } + ) +} + +pub trait Char { + fn is_alphabetic(&self) -> bool; + fn is_XID_start(&self) -> bool; + fn is_XID_continue(&self) -> bool; + fn is_lowercase(&self) -> bool; + fn is_uppercase(&self) -> bool; + fn is_whitespace(&self) -> bool; + fn is_alphanumeric(&self) -> bool; + fn is_digit(&self) -> bool; + fn is_digit_radix(&self, radix: uint) -> bool; + fn to_digit(&self, radix: uint) -> Option<uint>; + fn from_digit(num: uint, radix: uint) -> Option<char>; + fn escape_unicode(&self) -> ~str; + fn escape_default(&self) -> ~str; + fn len_utf8_bytes(&self) -> uint; +} + +impl Char for char { + fn is_alphabetic(&self) -> bool { is_alphabetic(*self) } + + fn is_XID_start(&self) -> bool { is_XID_start(*self) } + + fn is_XID_continue(&self) -> bool { is_XID_continue(*self) } + + fn is_lowercase(&self) -> bool { is_lowercase(*self) } + + fn is_uppercase(&self) -> bool { is_uppercase(*self) } + + fn is_whitespace(&self) -> bool { is_whitespace(*self) } + + fn is_alphanumeric(&self) -> bool { is_alphanumeric(*self) } + + fn is_digit(&self) -> bool { is_digit(*self) } + + fn is_digit_radix(&self, radix: uint) -> bool { is_digit_radix(*self, radix) } + + fn to_digit(&self, radix: uint) -> Option<uint> { to_digit(*self, radix) } + + fn from_digit(num: uint, radix: uint) -> Option<char> { from_digit(num, radix) } + + fn escape_unicode(&self) -> ~str { escape_unicode(*self) } + + fn escape_default(&self) -> ~str { escape_default(*self) } + + fn len_utf8_bytes(&self) -> uint { len_utf8_bytes(*self) } +} + +#[cfg(not(test))] +impl Eq for char { + #[inline(always)] + fn eq(&self, other: &char) -> bool { (*self) == (*other) } + #[inline(always)] + fn ne(&self, other: &char) -> bool { (*self) != (*other) } +} + +#[cfg(not(test))] +impl Ord for char { + #[inline(always)] + fn lt(&self, other: &char) -> bool { *self < *other } + #[inline(always)] + fn le(&self, other: &char) -> bool { *self <= *other } + #[inline(always)] + fn gt(&self, other: &char) -> bool { *self > *other } + #[inline(always)] + fn ge(&self, other: &char) -> bool { *self >= *other } +} + +#[test] +fn test_is_lowercase() { + assert!('a'.is_lowercase()); + assert!('ö'.is_lowercase()); + assert!('ß'.is_lowercase()); + assert!(!'Ü'.is_lowercase()); + assert!(!'P'.is_lowercase()); +} + +#[test] +fn test_is_uppercase() { + assert!(!'h'.is_uppercase()); + assert!(!'ä'.is_uppercase()); + assert!(!'ß'.is_uppercase()); + assert!('Ö'.is_uppercase()); + assert!('T'.is_uppercase()); +} + +#[test] +fn test_is_whitespace() { + assert!(' '.is_whitespace()); + assert!('\u2007'.is_whitespace()); + assert!('\t'.is_whitespace()); + assert!('\n'.is_whitespace()); + assert!(!'a'.is_whitespace()); + assert!(!'_'.is_whitespace()); + assert!(!'\u0000'.is_whitespace()); +} + +#[test] +fn test_to_digit() { + assert_eq!('0'.to_digit(10u), Some(0u)); + assert_eq!('1'.to_digit(2u), Some(1u)); + assert_eq!('2'.to_digit(3u), Some(2u)); + assert_eq!('9'.to_digit(10u), Some(9u)); + assert_eq!('a'.to_digit(16u), Some(10u)); + assert_eq!('A'.to_digit(16u), Some(10u)); + assert_eq!('b'.to_digit(16u), Some(11u)); + assert_eq!('B'.to_digit(16u), Some(11u)); + assert_eq!('z'.to_digit(36u), Some(35u)); + assert_eq!('Z'.to_digit(36u), Some(35u)); + assert_eq!(' '.to_digit(10u), None); + assert_eq!('$'.to_digit(36u), None); +} + +#[test] +fn test_is_digit() { + assert!('2'.is_digit()); + assert!('7'.is_digit()); + assert!(!'c'.is_digit()); + assert!(!'i'.is_digit()); + assert!(!'z'.is_digit()); + assert!(!'Q'.is_digit()); +} + +#[test] +fn test_escape_default() { + assert_eq!('\n'.escape_default(), ~"\\n"); + assert_eq!('\r'.escape_default(), ~"\\r"); + assert_eq!('\''.escape_default(), ~"\\'"); + assert_eq!('"'.escape_default(), ~"\\\""); + assert_eq!(' '.escape_default(), ~" "); + assert_eq!('a'.escape_default(), ~"a"); + assert_eq!('~'.escape_default(), ~"~"); + assert_eq!('\x00'.escape_default(), ~"\\x00"); + assert_eq!('\x1f'.escape_default(), ~"\\x1f"); + assert_eq!('\x7f'.escape_default(), ~"\\x7f"); + assert_eq!('\xff'.escape_default(), ~"\\xff"); + assert_eq!('\u011b'.escape_default(), ~"\\u011b"); + assert_eq!('\U0001d4b6'.escape_default(), ~"\\U0001d4b6"); +} + +#[test] +fn test_escape_unicode() { + assert_eq!('\x00'.escape_unicode(), ~"\\x00"); + assert_eq!('\n'.escape_unicode(), ~"\\x0a"); + assert_eq!(' '.escape_unicode(), ~"\\x20"); + assert_eq!('a'.escape_unicode(), ~"\\x61"); + assert_eq!('\u011b'.escape_unicode(), ~"\\u011b"); + assert_eq!('\U0001d4b6'.escape_unicode(), ~"\\U0001d4b6"); +} diff --git a/src/libstd/cleanup.rs b/src/libstd/cleanup.rs new file mode 100644 index 00000000000..5796be7b1e4 --- /dev/null +++ b/src/libstd/cleanup.rs @@ -0,0 +1,253 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; + +use libc::{c_char, c_void, intptr_t, uintptr_t}; +use ptr::mut_null; +use repr::BoxRepr; +use sys::TypeDesc; +use cast::transmute; +#[cfg(not(test))] use unstable::lang::clear_task_borrow_list; + +#[cfg(not(test))] use ptr::to_unsafe_ptr; + +/** + * Runtime structures + * + * NB: These must match the representation in the C++ runtime. + */ + +type DropGlue<'self> = &'self fn(**TypeDesc, *c_void); +type FreeGlue<'self> = &'self fn(**TypeDesc, *c_void); + +type TaskID = uintptr_t; + +struct StackSegment { priv opaque: () } +struct Scheduler { priv opaque: () } +struct SchedulerLoop { priv opaque: () } +struct Kernel { priv opaque: () } +struct Env { priv opaque: () } +struct AllocHeader { priv opaque: () } +struct MemoryRegion { priv opaque: () } + +#[cfg(target_arch="x86")] +#[cfg(target_arch="arm")] +struct Registers { + data: [u32, ..16] +} + +#[cfg(target_arch="mips")] +struct Registers { + data: [u32, ..32] +} + +#[cfg(target_arch="x86")] +#[cfg(target_arch="arm")] +#[cfg(target_arch="mips")] +struct Context { + regs: Registers, + next: *Context, + pad: [u32, ..3] +} + +#[cfg(target_arch="x86_64")] +struct Registers { + data: [u64, ..22] +} + +#[cfg(target_arch="x86_64")] +struct Context { + regs: Registers, + next: *Context, + pad: uintptr_t +} + +struct BoxedRegion { + env: *Env, + backing_region: *MemoryRegion, + live_allocs: *BoxRepr +} + +#[cfg(target_arch="x86")] +#[cfg(target_arch="arm")] +#[cfg(target_arch="mips")] +struct Task { + // Public fields + refcount: intptr_t, // 0 + id: TaskID, // 4 + pad: [u32, ..2], // 8 + ctx: Context, // 16 + stack_segment: *StackSegment, // 96 + runtime_sp: uintptr_t, // 100 + scheduler: *Scheduler, // 104 + scheduler_loop: *SchedulerLoop, // 108 + + // Fields known only to the runtime + kernel: *Kernel, // 112 + name: *c_char, // 116 + list_index: i32, // 120 + boxed_region: BoxedRegion // 128 +} + +#[cfg(target_arch="x86_64")] +struct Task { + // Public fields + refcount: intptr_t, + id: TaskID, + ctx: Context, + stack_segment: *StackSegment, + runtime_sp: uintptr_t, + scheduler: *Scheduler, + scheduler_loop: *SchedulerLoop, + + // Fields known only to the runtime + kernel: *Kernel, + name: *c_char, + list_index: i32, + boxed_region: BoxedRegion +} + +/* + * Box annihilation + * + * This runs at task death to free all boxes. + */ + +struct AnnihilateStats { + n_total_boxes: uint, + n_unique_boxes: uint, + n_bytes_freed: uint +} + +unsafe fn each_live_alloc(read_next_before: bool, + f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) -> bool { + //! Walks the internal list of allocations + + use managed; + + let task: *Task = transmute(rustrt::rust_get_task()); + let box = (*task).boxed_region.live_allocs; + let mut box: *mut BoxRepr = transmute(copy box); + while box != mut_null() { + let next_before = transmute(copy (*box).header.next); + let uniq = + (*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE; + + if !f(box, uniq) { + return false; + } + + if read_next_before { + box = next_before; + } else { + box = transmute(copy (*box).header.next); + } + } + return true; +} + +#[cfg(unix)] +fn debug_mem() -> bool { + ::rt::env::get().debug_mem +} + +#[cfg(windows)] +fn debug_mem() -> bool { + false +} + +/// Destroys all managed memory (i.e. @ boxes) held by the current task. +#[cfg(not(test))] +#[lang="annihilate"] +pub unsafe fn annihilate() { + use unstable::lang::local_free; + use io::WriterUtil; + use io; + use libc; + use sys; + use managed; + + let mut stats = AnnihilateStats { + n_total_boxes: 0, + n_unique_boxes: 0, + n_bytes_freed: 0 + }; + + // Quick hack: we need to free this list upon task exit, and this + // is a convenient place to do it. + clear_task_borrow_list(); + + // Pass 1: Make all boxes immortal. + // + // In this pass, nothing gets freed, so it does not matter whether + // we read the next field before or after the callback. + for each_live_alloc(true) |box, uniq| { + stats.n_total_boxes += 1; + if uniq { + stats.n_unique_boxes += 1; + } else { + (*box).header.ref_count = managed::raw::RC_IMMORTAL; + } + } + + // Pass 2: Drop all boxes. + // + // In this pass, unique-managed boxes may get freed, but not + // managed boxes, so we must read the `next` field *after* the + // callback, as the original value may have been freed. + for each_live_alloc(false) |box, uniq| { + if !uniq { + let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc); + let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0)); + drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data)); + } + } + + // Pass 3: Free all boxes. + // + // In this pass, managed boxes may get freed (but not + // unique-managed boxes, though I think that none of those are + // left), so we must read the `next` field before, since it will + // not be valid after. + for each_live_alloc(true) |box, uniq| { + if !uniq { + stats.n_bytes_freed += + (*((*box).header.type_desc)).size + + sys::size_of::<BoxRepr>(); + local_free(transmute(box)); + } + } + + if debug_mem() { + // We do logging here w/o allocation. + let dbg = libc::STDERR_FILENO as io::fd_t; + dbg.write_str("annihilator stats:"); + dbg.write_str("\n total_boxes: "); + dbg.write_uint(stats.n_total_boxes); + dbg.write_str("\n unique_boxes: "); + dbg.write_uint(stats.n_unique_boxes); + dbg.write_str("\n bytes_freed: "); + dbg.write_uint(stats.n_bytes_freed); + dbg.write_str("\n"); + } +} + +/// Bindings to the runtime +pub mod rustrt { + use libc::c_void; + + #[link_name = "rustrt"] + pub extern { + #[rust_stack] + // FIXME (#4386): Unable to make following method private. + pub unsafe fn rust_get_task() -> *c_void; + } +} diff --git a/src/libstd/clone.rs b/src/libstd/clone.rs new file mode 100644 index 00000000000..2965b31a8c3 --- /dev/null +++ b/src/libstd/clone.rs @@ -0,0 +1,184 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! The Clone trait for types that cannot be "implicitly copied" + +In Rust, some simple types are "implicitly copyable" and when you +assign them or pass them as arguments, the receiver will get a copy, +leaving the original value in place. These types do not require +allocation to copy and do not have finalizers (i.e. they do not +contain owned boxes or implement `Drop`), so the compiler considers +them cheap and safe to copy and automatically implements the `Copy` +trait for them. For other types copies must be made explicitly, +by convention implementing the `Clone` trait and calling the +`clone` method. + +*/ + +use core::kinds::Const; + +pub trait Clone { + /// Returns a copy of the value. The contents of owned pointers + /// are copied to maintain uniqueness, while the contents of + /// managed pointers are not copied. + fn clone(&self) -> Self; +} + +impl<T: Clone> Clone for ~T { + /// Return a deep copy of the owned box. + #[inline(always)] + fn clone(&self) -> ~T { ~(**self).clone() } +} + +impl<T> Clone for @T { + /// Return a shallow copy of the managed box. + #[inline(always)] + fn clone(&self) -> @T { *self } +} + +impl<T> Clone for @mut T { + /// Return a shallow copy of the managed box. + #[inline(always)] + fn clone(&self) -> @mut T { *self } +} + +impl<'self, T> Clone for &'self T { + /// Return a shallow copy of the borrowed pointer. + #[inline(always)] + fn clone(&self) -> &'self T { *self } +} + +macro_rules! clone_impl( + ($t:ty) => { + impl Clone for $t { + /// Return a deep copy of the value. + #[inline(always)] + fn clone(&self) -> $t { *self } + } + } +) + +clone_impl!(int) +clone_impl!(i8) +clone_impl!(i16) +clone_impl!(i32) +clone_impl!(i64) + +clone_impl!(uint) +clone_impl!(u8) +clone_impl!(u16) +clone_impl!(u32) +clone_impl!(u64) + +clone_impl!(float) +clone_impl!(f32) +clone_impl!(f64) + +clone_impl!(()) +clone_impl!(bool) +clone_impl!(char) + +pub trait DeepClone { + /// Return a deep copy of the value. Unlike `Clone`, the contents of shared pointer types + /// *are* copied. Note that this is currently unimplemented for managed boxes, as + /// it would need to handle cycles, but it is implemented for other smart-pointer types. + fn deep_clone(&self) -> Self; +} + +impl<T: DeepClone> DeepClone for ~T { + /// Return a deep copy of the owned box. + #[inline(always)] + fn deep_clone(&self) -> ~T { ~(**self).deep_clone() } +} + +// FIXME: #6525: should also be implemented for `T: Owned + DeepClone` +impl<T: Const + DeepClone> DeepClone for @T { + /// Return a deep copy of the managed box. The `Const` trait is required to prevent performing + /// a deep clone of a potentially cyclical type. + #[inline(always)] + fn deep_clone(&self) -> @T { @(**self).deep_clone() } +} + +// FIXME: #6525: should also be implemented for `T: Owned + DeepClone` +impl<T: Const + DeepClone> DeepClone for @mut T { + /// Return a deep copy of the managed box. The `Const` trait is required to prevent performing + /// a deep clone of a potentially cyclical type. + #[inline(always)] + fn deep_clone(&self) -> @mut T { @mut (**self).deep_clone() } +} + +macro_rules! deep_clone_impl( + ($t:ty) => { + impl DeepClone for $t { + /// Return a deep copy of the value. + #[inline(always)] + fn deep_clone(&self) -> $t { *self } + } + } +) + +deep_clone_impl!(int) +deep_clone_impl!(i8) +deep_clone_impl!(i16) +deep_clone_impl!(i32) +deep_clone_impl!(i64) + +deep_clone_impl!(uint) +deep_clone_impl!(u8) +deep_clone_impl!(u16) +deep_clone_impl!(u32) +deep_clone_impl!(u64) + +deep_clone_impl!(float) +deep_clone_impl!(f32) +deep_clone_impl!(f64) + +deep_clone_impl!(()) +deep_clone_impl!(bool) +deep_clone_impl!(char) + +#[test] +fn test_owned_clone() { + let a = ~5i; + let b: ~int = a.clone(); + assert_eq!(a, b); +} + +#[test] +fn test_managed_clone() { + let a = @5i; + let b: @int = a.clone(); + assert_eq!(a, b); +} + +#[test] +fn test_managed_mut_deep_clone() { + let x = @mut 5i; + let y: @mut int = x.deep_clone(); + *x = 20; + assert_eq!(*y, 5); +} + +#[test] +fn test_managed_mut_clone() { + let a = @mut 5i; + let b: @mut int = a.clone(); + assert_eq!(a, b); + *b = 10; + assert_eq!(a, b); +} + +#[test] +fn test_borrowed_clone() { + let x = 5i; + let y: &int = &x; + let z: &int = (&y).clone(); + assert_eq!(*z, 5); +} diff --git a/src/libstd/cmp.rs b/src/libstd/cmp.rs new file mode 100644 index 00000000000..ca9c49b2c06 --- /dev/null +++ b/src/libstd/cmp.rs @@ -0,0 +1,265 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +The `Ord` and `Eq` comparison traits + +This module contains the definition of both `Ord` and `Eq` which define +the common interfaces for doing comparison. Both are language items +that the compiler uses to implement the comparison operators. Rust code +may implement `Ord` to overload the `<`, `<=`, `>`, and `>=` operators, +and `Eq` to overload the `==` and `!=` operators. + +*/ + +/** +* Trait for values that can be compared for equality and inequality. +* +* This trait allows partial equality, where types can be unordered instead of strictly equal or +* unequal. For example, with the built-in floating-point types `a == b` and `a != b` will both +* evaluate to false if either `a` or `b` is NaN (cf. IEEE 754-2008 section 5.11). +* +* Eventually, this will be implemented by default for types that implement `TotalEq`. +*/ +#[lang="eq"] +pub trait Eq { + fn eq(&self, other: &Self) -> bool; + fn ne(&self, other: &Self) -> bool; +} + +/// Trait for equality comparisons where `a == b` and `a != b` are strict inverses. +pub trait TotalEq { + fn equals(&self, other: &Self) -> bool; +} + +macro_rules! totaleq_impl( + ($t:ty) => { + impl TotalEq for $t { + #[inline(always)] + fn equals(&self, other: &$t) -> bool { *self == *other } + } + } +) + +totaleq_impl!(bool) + +totaleq_impl!(u8) +totaleq_impl!(u16) +totaleq_impl!(u32) +totaleq_impl!(u64) + +totaleq_impl!(i8) +totaleq_impl!(i16) +totaleq_impl!(i32) +totaleq_impl!(i64) + +totaleq_impl!(int) +totaleq_impl!(uint) + +totaleq_impl!(char) + +/// Trait for testing approximate equality +pub trait ApproxEq<Eps> { + fn approx_epsilon() -> Eps; + fn approx_eq(&self, other: &Self) -> bool; + fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool; +} + +#[deriving(Clone, Eq)] +pub enum Ordering { Less = -1, Equal = 0, Greater = 1 } + +/// Trait for types that form a total order +pub trait TotalOrd: TotalEq { + fn cmp(&self, other: &Self) -> Ordering; +} + +impl TotalOrd for Ordering { + #[inline(always)] + fn cmp(&self, other: &Ordering) -> Ordering { + (*self as int).cmp(&(*other as int)) + } +} + +impl Ord for Ordering { + #[inline(always)] + fn lt(&self, other: &Ordering) -> bool { (*self as int) < (*other as int) } + #[inline(always)] + fn le(&self, other: &Ordering) -> bool { (*self as int) <= (*other as int) } + #[inline(always)] + fn gt(&self, other: &Ordering) -> bool { (*self as int) > (*other as int) } + #[inline(always)] + fn ge(&self, other: &Ordering) -> bool { (*self as int) >= (*other as int) } +} + +macro_rules! totalord_impl( + ($t:ty) => { + impl TotalOrd for $t { + #[inline(always)] + fn cmp(&self, other: &$t) -> Ordering { + if *self < *other { Less } + else if *self > *other { Greater } + else { Equal } + } + } + } +) + +totalord_impl!(u8) +totalord_impl!(u16) +totalord_impl!(u32) +totalord_impl!(u64) + +totalord_impl!(i8) +totalord_impl!(i16) +totalord_impl!(i32) +totalord_impl!(i64) + +totalord_impl!(int) +totalord_impl!(uint) + +totalord_impl!(char) + +/// Compares (a1, b1) against (a2, b2), where the a values are more significant. +pub fn cmp2<A:TotalOrd,B:TotalOrd>( + a1: &A, b1: &B, + a2: &A, b2: &B) -> Ordering +{ + match a1.cmp(a2) { + Less => Less, + Greater => Greater, + Equal => b1.cmp(b2) + } +} + +/** +Return `o1` if it is not `Equal`, otherwise `o2`. Simulates the +lexical ordering on a type `(int, int)`. +*/ +// used in deriving code in libsyntax +#[inline(always)] +pub fn lexical_ordering(o1: Ordering, o2: Ordering) -> Ordering { + match o1 { + Equal => o2, + _ => o1 + } +} + +/** +* Trait for values that can be compared for a sort-order. +* +* Eventually this may be simplified to only require +* an `le` method, with the others generated from +* default implementations. However it should remain +* possible to implement the others separately, for +* compatibility with floating-point NaN semantics +* (cf. IEEE 754-2008 section 5.11). +*/ +#[lang="ord"] +pub trait Ord { + fn lt(&self, other: &Self) -> bool; + fn le(&self, other: &Self) -> bool; + fn ge(&self, other: &Self) -> bool; + fn gt(&self, other: &Self) -> bool; +} + +#[inline(always)] +pub fn lt<T:Ord>(v1: &T, v2: &T) -> bool { + (*v1).lt(v2) +} + +#[inline(always)] +pub fn le<T:Ord>(v1: &T, v2: &T) -> bool { + (*v1).le(v2) +} + +#[inline(always)] +pub fn eq<T:Eq>(v1: &T, v2: &T) -> bool { + (*v1).eq(v2) +} + +#[inline(always)] +pub fn ne<T:Eq>(v1: &T, v2: &T) -> bool { + (*v1).ne(v2) +} + +#[inline(always)] +pub fn ge<T:Ord>(v1: &T, v2: &T) -> bool { + (*v1).ge(v2) +} + +#[inline(always)] +pub fn gt<T:Ord>(v1: &T, v2: &T) -> bool { + (*v1).gt(v2) +} + +/// The equivalence relation. Two values may be equivalent even if they are +/// of different types. The most common use case for this relation is +/// container types; e.g. it is often desirable to be able to use `&str` +/// values to look up entries in a container with `~str` keys. +pub trait Equiv<T> { + fn equiv(&self, other: &T) -> bool; +} + +#[inline(always)] +pub fn min<T:Ord>(v1: T, v2: T) -> T { + if v1 < v2 { v1 } else { v2 } +} + +#[inline(always)] +pub fn max<T:Ord>(v1: T, v2: T) -> T { + if v1 > v2 { v1 } else { v2 } +} + +#[cfg(test)] +mod test { + use super::lexical_ordering; + + #[test] + fn test_int_totalord() { + assert_eq!(5.cmp(&10), Less); + assert_eq!(10.cmp(&5), Greater); + assert_eq!(5.cmp(&5), Equal); + assert_eq!((-5).cmp(&12), Less); + assert_eq!(12.cmp(-5), Greater); + } + + #[test] + fn test_cmp2() { + assert_eq!(cmp2(1, 2, 3, 4), Less); + assert_eq!(cmp2(3, 2, 3, 4), Less); + assert_eq!(cmp2(5, 2, 3, 4), Greater); + assert_eq!(cmp2(5, 5, 5, 4), Greater); + } + + #[test] + fn test_int_totaleq() { + assert!(5.equals(&5)); + assert!(!2.equals(&17)); + } + + #[test] + fn test_ordering_order() { + assert!(Less < Equal); + assert_eq!(Greater.cmp(&Less), Greater); + } + + #[test] + fn test_lexical_ordering() { + fn t(o1: Ordering, o2: Ordering, e: Ordering) { + assert_eq!(lexical_ordering(o1, o2), e); + } + for [Less, Equal, Greater].each |&o| { + t(Less, o, Less); + t(Equal, o, o); + t(Greater, o, Greater); + } + } +} diff --git a/src/libstd/comm.rs b/src/libstd/comm.rs index 28d3d329b26..59eb915c239 100644 --- a/src/libstd/comm.rs +++ b/src/libstd/comm.rs @@ -9,102 +9,802 @@ // except according to those terms. /*! +Message passing +*/ -Higher level communication abstractions. +use cast::{transmute, transmute_mut}; +use container::Container; +use either::{Either, Left, Right}; +use kinds::Owned; +use option::{Option, Some, None}; +use uint; +use vec; +use vec::OwnedVector; +use util::replace; +use unstable::sync::{Exclusive, exclusive}; +use rtcomm = rt::comm; +use rt; -*/ +use pipes::{wait_many, PacketHeader}; + +// FIXME #5160: Making this public exposes some plumbing from +// pipes. Needs some refactoring +pub use pipes::Selectable; -use core::comm::{GenericChan, GenericSmartChan, GenericPort}; -use core::comm::{Chan, Port, Selectable, Peekable}; -use core::pipes; +/// A trait for things that can send multiple messages. +pub trait GenericChan<T> { + /// Sends a message. + fn send(&self, x: T); +} + +/// Things that can send multiple messages and can detect when the receiver +/// is closed +pub trait GenericSmartChan<T> { + /// Sends a message, or report if the receiver has closed the connection. + fn try_send(&self, x: T) -> bool; +} + +/// A trait for things that can receive multiple messages. +pub trait GenericPort<T> { + /// Receives a message, or fails if the connection closes. + fn recv(&self) -> T; + + /** Receives a message, or returns `none` if + the connection is closed or closes. + */ + fn try_recv(&self) -> Option<T>; +} + +/// Ports that can `peek` +pub trait Peekable<T> { + /// Returns true if a message is available + fn peek(&self) -> bool; +} -/// An extension of `pipes::stream` that allows both sending and receiving. -pub struct DuplexStream<T, U> { - priv chan: Chan<T>, - priv port: Port<U>, +/// An endpoint that can send many messages. +pub struct Chan<T> { + inner: Either<pipesy::Chan<T>, rtcomm::Chan<T>> } -// Allow these methods to be used without import: -pub impl<T:Owned,U:Owned> DuplexStream<T, U> { +/// An endpoint that can receive many messages. +pub struct Port<T> { + inner: Either<pipesy::Port<T>, rtcomm::Port<T>> +} + +/** Creates a `(Port, Chan)` pair. + +These allow sending or receiving an unlimited number of messages. + +*/ +pub fn stream<T:Owned>() -> (Port<T>, Chan<T>) { + let (port, chan) = match rt::context() { + rt::OldTaskContext => match pipesy::stream() { + (p, c) => (Left(p), Left(c)) + }, + _ => match rtcomm::stream() { + (p, c) => (Right(p), Right(c)) + } + }; + let port = Port { inner: port }; + let chan = Chan { inner: chan }; + return (port, chan); +} + +impl<T: Owned> GenericChan<T> for Chan<T> { fn send(&self, x: T) { - self.chan.send(x) + match self.inner { + Left(ref chan) => chan.send(x), + Right(ref chan) => chan.send(x) + } } +} + +impl<T: Owned> GenericSmartChan<T> for Chan<T> { fn try_send(&self, x: T) -> bool { - self.chan.try_send(x) + match self.inner { + Left(ref chan) => chan.try_send(x), + Right(ref chan) => chan.try_send(x) + } } - fn recv(&self, ) -> U { - self.port.recv() +} + +impl<T: Owned> GenericPort<T> for Port<T> { + fn recv(&self) -> T { + match self.inner { + Left(ref port) => port.recv(), + Right(ref port) => port.recv() + } } - fn try_recv(&self) -> Option<U> { - self.port.try_recv() + + fn try_recv(&self) -> Option<T> { + match self.inner { + Left(ref port) => port.try_recv(), + Right(ref port) => port.try_recv() + } } +} + +impl<T: Owned> Peekable<T> for Port<T> { fn peek(&self) -> bool { - self.port.peek() + match self.inner { + Left(ref port) => port.peek(), + Right(ref port) => port.peek() + } + } +} + +impl<T: Owned> Selectable for Port<T> { + fn header(&mut self) -> *mut PacketHeader { + match self.inner { + Left(ref mut port) => port.header(), + Right(_) => fail!("can't select on newsched ports") + } + } +} + +/// Treat many ports as one. +#[unsafe_mut_field(ports)] +pub struct PortSet<T> { + ports: ~[pipesy::Port<T>], +} + +pub impl<T: Owned> PortSet<T> { + fn new() -> PortSet<T> { + PortSet { + ports: ~[] + } + } + + fn add(&self, port: Port<T>) { + let Port { inner } = port; + let port = match inner { + Left(p) => p, + Right(_) => fail!("PortSet not implemented") + }; + unsafe { + let self_ports = transmute_mut(&self.ports); + self_ports.push(port) + } + } + + fn chan(&self) -> Chan<T> { + let (po, ch) = stream(); + self.add(po); + ch + } +} + +impl<T:Owned> GenericPort<T> for PortSet<T> { + fn try_recv(&self) -> Option<T> { + unsafe { + let self_ports = transmute_mut(&self.ports); + let mut result = None; + // we have to swap the ports array so we aren't borrowing + // aliasable mutable memory. + let mut ports = replace(self_ports, ~[]); + while result.is_none() && ports.len() > 0 { + let i = wait_many(ports); + match ports[i].try_recv() { + Some(m) => { + result = Some(m); + } + None => { + // Remove this port. + let _ = ports.swap_remove(i); + } + } + } + *self_ports = ports; + result + } + } + fn recv(&self) -> T { + self.try_recv().expect("port_set: endpoints closed") } } -impl<T:Owned,U:Owned> GenericChan<T> for DuplexStream<T, U> { +impl<T: Owned> Peekable<T> for PortSet<T> { + fn peek(&self) -> bool { + // It'd be nice to use self.port.each, but that version isn't + // pure. + for uint::range(0, vec::uniq_len(&const self.ports)) |i| { + let port: &pipesy::Port<T> = &self.ports[i]; + if port.peek() { + return true; + } + } + false + } +} + +/// A channel that can be shared between many senders. +pub struct SharedChan<T> { + ch: Exclusive<pipesy::Chan<T>> +} + +impl<T: Owned> SharedChan<T> { + /// Converts a `chan` into a `shared_chan`. + pub fn new(c: Chan<T>) -> SharedChan<T> { + let Chan { inner } = c; + let c = match inner { + Left(c) => c, + Right(_) => fail!("SharedChan not implemented") + }; + SharedChan { ch: exclusive(c) } + } +} + +impl<T: Owned> GenericChan<T> for SharedChan<T> { fn send(&self, x: T) { - self.chan.send(x) + let mut xx = Some(x); + do self.ch.with_imm |chan| { + let x = replace(&mut xx, None); + chan.send(x.unwrap()) + } } } -impl<T:Owned,U:Owned> GenericSmartChan<T> for DuplexStream<T, U> { +impl<T: Owned> GenericSmartChan<T> for SharedChan<T> { fn try_send(&self, x: T) -> bool { - self.chan.try_send(x) + let mut xx = Some(x); + do self.ch.with_imm |chan| { + let x = replace(&mut xx, None); + chan.try_send(x.unwrap()) + } + } +} + +impl<T: Owned> ::clone::Clone for SharedChan<T> { + fn clone(&self) -> SharedChan<T> { + SharedChan { ch: self.ch.clone() } } } -impl<T:Owned,U:Owned> GenericPort<U> for DuplexStream<T, U> { - fn recv(&self) -> U { - self.port.recv() +pub struct PortOne<T> { + inner: Either<pipesy::PortOne<T>, rtcomm::PortOne<T>> +} + +pub struct ChanOne<T> { + inner: Either<pipesy::ChanOne<T>, rtcomm::ChanOne<T>> +} + +pub fn oneshot<T: Owned>() -> (PortOne<T>, ChanOne<T>) { + let (port, chan) = match rt::context() { + rt::OldTaskContext => match pipesy::oneshot() { + (p, c) => (Left(p), Left(c)), + }, + _ => match rtcomm::oneshot() { + (p, c) => (Right(p), Right(c)) + } + }; + let port = PortOne { inner: port }; + let chan = ChanOne { inner: chan }; + return (port, chan); +} + +impl<T: Owned> PortOne<T> { + pub fn recv(self) -> T { + let PortOne { inner } = self; + match inner { + Left(p) => p.recv(), + Right(p) => p.recv() + } } - fn try_recv(&self) -> Option<U> { - self.port.try_recv() + pub fn try_recv(self) -> Option<T> { + let PortOne { inner } = self; + match inner { + Left(p) => p.try_recv(), + Right(p) => p.try_recv() + } } } -impl<T:Owned,U:Owned> Peekable<U> for DuplexStream<T, U> { - fn peek(&self) -> bool { - self.port.peek() +impl<T: Owned> ChanOne<T> { + pub fn send(self, data: T) { + let ChanOne { inner } = self; + match inner { + Left(p) => p.send(data), + Right(p) => p.send(data) + } + } + + pub fn try_send(self, data: T) -> bool { + let ChanOne { inner } = self; + match inner { + Left(p) => p.try_send(data), + Right(p) => p.try_send(data) + } + } +} + +pub fn recv_one<T: Owned>(port: PortOne<T>) -> T { + let PortOne { inner } = port; + match inner { + Left(p) => pipesy::recv_one(p), + Right(p) => p.recv() + } +} + +pub fn try_recv_one<T: Owned>(port: PortOne<T>) -> Option<T> { + let PortOne { inner } = port; + match inner { + Left(p) => pipesy::try_recv_one(p), + Right(p) => p.try_recv() + } +} + +pub fn send_one<T: Owned>(chan: ChanOne<T>, data: T) { + let ChanOne { inner } = chan; + match inner { + Left(c) => pipesy::send_one(c, data), + Right(c) => c.send(data) + } +} + +pub fn try_send_one<T: Owned>(chan: ChanOne<T>, data: T) -> bool { + let ChanOne { inner } = chan; + match inner { + Left(c) => pipesy::try_send_one(c, data), + Right(c) => c.try_send(data) + } +} + +mod pipesy { + + use kinds::Owned; + use option::{Option, Some, None}; + use pipes::{recv, try_recv, peek, PacketHeader}; + use super::{GenericChan, GenericSmartChan, GenericPort, Peekable, Selectable}; + use cast::transmute_mut; + use util::replace; + + /*proto! oneshot ( + Oneshot:send<T:Owned> { + send(T) -> ! + } + )*/ + + #[allow(non_camel_case_types)] + pub mod oneshot { + priv use core::kinds::Owned; + use ptr::to_mut_unsafe_ptr; + + pub fn init<T: Owned>() -> (client::Oneshot<T>, server::Oneshot<T>) { + pub use core::pipes::HasBuffer; + + let buffer = ~::core::pipes::Buffer { + header: ::core::pipes::BufferHeader(), + data: __Buffer { + Oneshot: ::core::pipes::mk_packet::<Oneshot<T>>() + }, + }; + do ::core::pipes::entangle_buffer(buffer) |buffer, data| { + data.Oneshot.set_buffer(buffer); + to_mut_unsafe_ptr(&mut data.Oneshot) + } + } + #[allow(non_camel_case_types)] + pub enum Oneshot<T> { pub send(T), } + #[allow(non_camel_case_types)] + pub struct __Buffer<T> { + Oneshot: ::core::pipes::Packet<Oneshot<T>>, + } + + #[allow(non_camel_case_types)] + pub mod client { + + priv use core::kinds::Owned; + + #[allow(non_camel_case_types)] + pub fn try_send<T: Owned>(pipe: Oneshot<T>, x_0: T) -> + ::core::option::Option<()> { + { + use super::send; + let message = send(x_0); + if ::core::pipes::send(pipe, message) { + ::core::pipes::rt::make_some(()) + } else { ::core::pipes::rt::make_none() } + } + } + + #[allow(non_camel_case_types)] + pub fn send<T: Owned>(pipe: Oneshot<T>, x_0: T) { + { + use super::send; + let message = send(x_0); + ::core::pipes::send(pipe, message); + } + } + + #[allow(non_camel_case_types)] + pub type Oneshot<T> = + ::core::pipes::SendPacketBuffered<super::Oneshot<T>, + super::__Buffer<T>>; + } + + #[allow(non_camel_case_types)] + pub mod server { + #[allow(non_camel_case_types)] + pub type Oneshot<T> = + ::core::pipes::RecvPacketBuffered<super::Oneshot<T>, + super::__Buffer<T>>; + } + } + + /// The send end of a oneshot pipe. + pub struct ChanOne<T> { + contents: oneshot::client::Oneshot<T> + } + + impl<T> ChanOne<T> { + pub fn new(contents: oneshot::client::Oneshot<T>) -> ChanOne<T> { + ChanOne { + contents: contents + } + } + } + + /// The receive end of a oneshot pipe. + pub struct PortOne<T> { + contents: oneshot::server::Oneshot<T> + } + + impl<T> PortOne<T> { + pub fn new(contents: oneshot::server::Oneshot<T>) -> PortOne<T> { + PortOne { + contents: contents + } + } + } + + /// Initialiase a (send-endpoint, recv-endpoint) oneshot pipe pair. + pub fn oneshot<T: Owned>() -> (PortOne<T>, ChanOne<T>) { + let (chan, port) = oneshot::init(); + (PortOne::new(port), ChanOne::new(chan)) + } + + pub impl<T: Owned> PortOne<T> { + fn recv(self) -> T { recv_one(self) } + fn try_recv(self) -> Option<T> { try_recv_one(self) } + fn unwrap(self) -> oneshot::server::Oneshot<T> { + match self { + PortOne { contents: s } => s + } + } + } + + pub impl<T: Owned> ChanOne<T> { + fn send(self, data: T) { send_one(self, data) } + fn try_send(self, data: T) -> bool { try_send_one(self, data) } + fn unwrap(self) -> oneshot::client::Oneshot<T> { + match self { + ChanOne { contents: s } => s + } + } + } + + /** + * Receive a message from a oneshot pipe, failing if the connection was + * closed. + */ + pub fn recv_one<T: Owned>(port: PortOne<T>) -> T { + match port { + PortOne { contents: port } => { + let oneshot::send(message) = recv(port); + message + } + } + } + + /// Receive a message from a oneshot pipe unless the connection was closed. + pub fn try_recv_one<T: Owned> (port: PortOne<T>) -> Option<T> { + match port { + PortOne { contents: port } => { + let message = try_recv(port); + + if message.is_none() { + None + } else { + let oneshot::send(message) = message.unwrap(); + Some(message) + } + } + } + } + + /// Send a message on a oneshot pipe, failing if the connection was closed. + pub fn send_one<T: Owned>(chan: ChanOne<T>, data: T) { + match chan { + ChanOne { contents: chan } => oneshot::client::send(chan, data), + } + } + + /** + * Send a message on a oneshot pipe, or return false if the connection was + * closed. + */ + pub fn try_send_one<T: Owned>(chan: ChanOne<T>, data: T) -> bool { + match chan { + ChanOne { contents: chan } => { + oneshot::client::try_send(chan, data).is_some() + } + } + } + + // Streams - Make pipes a little easier in general. + + /*proto! streamp ( + Open:send<T: Owned> { + data(T) -> Open<T> + } + )*/ + + #[allow(non_camel_case_types)] + pub mod streamp { + priv use core::kinds::Owned; + + pub fn init<T: Owned>() -> (client::Open<T>, server::Open<T>) { + pub use core::pipes::HasBuffer; + ::core::pipes::entangle() + } + + #[allow(non_camel_case_types)] + pub enum Open<T> { pub data(T, server::Open<T>), } + + #[allow(non_camel_case_types)] + pub mod client { + priv use core::kinds::Owned; + + #[allow(non_camel_case_types)] + pub fn try_data<T: Owned>(pipe: Open<T>, x_0: T) -> + ::core::option::Option<Open<T>> { + { + use super::data; + let (c, s) = ::core::pipes::entangle(); + let message = data(x_0, s); + if ::core::pipes::send(pipe, message) { + ::core::pipes::rt::make_some(c) + } else { ::core::pipes::rt::make_none() } + } + } + + #[allow(non_camel_case_types)] + pub fn data<T: Owned>(pipe: Open<T>, x_0: T) -> Open<T> { + { + use super::data; + let (c, s) = ::core::pipes::entangle(); + let message = data(x_0, s); + ::core::pipes::send(pipe, message); + c + } + } + + #[allow(non_camel_case_types)] + pub type Open<T> = ::core::pipes::SendPacket<super::Open<T>>; + } + + #[allow(non_camel_case_types)] + pub mod server { + #[allow(non_camel_case_types)] + pub type Open<T> = ::core::pipes::RecvPacket<super::Open<T>>; + } + } + + /// An endpoint that can send many messages. + #[unsafe_mut_field(endp)] + pub struct Chan<T> { + endp: Option<streamp::client::Open<T>> + } + + /// An endpoint that can receive many messages. + #[unsafe_mut_field(endp)] + pub struct Port<T> { + endp: Option<streamp::server::Open<T>>, + } + + /** Creates a `(Port, Chan)` pair. + + These allow sending or receiving an unlimited number of messages. + + */ + pub fn stream<T:Owned>() -> (Port<T>, Chan<T>) { + let (c, s) = streamp::init(); + + (Port { + endp: Some(s) + }, Chan { + endp: Some(c) + }) + } + + impl<T: Owned> GenericChan<T> for Chan<T> { + #[inline(always)] + fn send(&self, x: T) { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + *self_endp = Some(streamp::client::data(endp.unwrap(), x)) + } + } + } + + impl<T: Owned> GenericSmartChan<T> for Chan<T> { + #[inline(always)] + fn try_send(&self, x: T) -> bool { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + match streamp::client::try_data(endp.unwrap(), x) { + Some(next) => { + *self_endp = Some(next); + true + } + None => false + } + } + } + } + + impl<T: Owned> GenericPort<T> for Port<T> { + #[inline(always)] + fn recv(&self) -> T { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + let streamp::data(x, endp) = recv(endp.unwrap()); + *self_endp = Some(endp); + x + } + } + + #[inline(always)] + fn try_recv(&self) -> Option<T> { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + match try_recv(endp.unwrap()) { + Some(streamp::data(x, endp)) => { + *self_endp = Some(endp); + Some(x) + } + None => None + } + } + } + } + + impl<T: Owned> Peekable<T> for Port<T> { + #[inline(always)] + fn peek(&self) -> bool { + unsafe { + let self_endp = transmute_mut(&self.endp); + let mut endp = replace(self_endp, None); + let peek = match endp { + Some(ref mut endp) => peek(endp), + None => fail!("peeking empty stream") + }; + *self_endp = endp; + peek + } + } + } + + impl<T: Owned> Selectable for Port<T> { + fn header(&mut self) -> *mut PacketHeader { + match self.endp { + Some(ref mut endp) => endp.header(), + None => fail!("peeking empty stream") + } } } -impl<T:Owned,U:Owned> Selectable for DuplexStream<T, U> { - fn header(&mut self) -> *mut pipes::PacketHeader { - self.port.header() +} + +/// Returns the index of an endpoint that is ready to receive. +pub fn selecti<T: Selectable>(endpoints: &mut [T]) -> uint { + wait_many(endpoints) +} + +/// Returns 0 or 1 depending on which endpoint is ready to receive +pub fn select2i<A:Selectable, B:Selectable>(a: &mut A, b: &mut B) + -> Either<(), ()> { + let mut endpoints = [ a.header(), b.header() ]; + match wait_many(endpoints) { + 0 => Left(()), + 1 => Right(()), + _ => fail!("wait returned unexpected index"), } } -/// Creates a bidirectional stream. -pub fn DuplexStream<T:Owned,U:Owned>() - -> (DuplexStream<T, U>, DuplexStream<U, T>) -{ - let (p1, c2) = comm::stream(); - let (p2, c1) = comm::stream(); - (DuplexStream { - chan: c1, - port: p1 - }, - DuplexStream { - chan: c2, - port: p2 - }) +/// Receive a message from one of two endpoints. +pub trait Select2<T: Owned, U: Owned> { + /// Receive a message or return `None` if a connection closes. + fn try_select(&mut self) -> Either<Option<T>, Option<U>>; + /// Receive a message or fail if a connection closes. + fn select(&mut self) -> Either<T, U>; +} + +impl<T:Owned, + U:Owned, + Left:Selectable + GenericPort<T>, + Right:Selectable + GenericPort<U>> + Select2<T, U> + for (Left, Right) { + fn select(&mut self) -> Either<T, U> { + // XXX: Bad borrow check workaround. + unsafe { + let this: &(Left, Right) = transmute(self); + match *this { + (ref lp, ref rp) => { + let lp: &mut Left = transmute(lp); + let rp: &mut Right = transmute(rp); + match select2i(lp, rp) { + Left(()) => Left(lp.recv()), + Right(()) => Right(rp.recv()), + } + } + } + } + } + + fn try_select(&mut self) -> Either<Option<T>, Option<U>> { + // XXX: Bad borrow check workaround. + unsafe { + let this: &(Left, Right) = transmute(self); + match *this { + (ref lp, ref rp) => { + let lp: &mut Left = transmute(lp); + let rp: &mut Right = transmute(rp); + match select2i(lp, rp) { + Left(()) => Left (lp.try_recv()), + Right(()) => Right(rp.try_recv()), + } + } + } + } + } } #[cfg(test)] mod test { - use comm::DuplexStream; + use either::Right; + use super::{Chan, Port, oneshot, stream}; + + #[test] + fn test_select2() { + let (p1, c1) = stream(); + let (p2, c2) = stream(); + + c1.send(~"abc"); + + let mut tuple = (p1, p2); + match tuple.select() { + Right(_) => fail!(), + _ => (), + } + + c2.send(123); + } + + #[test] + fn test_oneshot() { + let (p, c) = oneshot(); + + c.send(()); + + p.recv() + } #[test] - pub fn DuplexStream1() { - let (left, right) = DuplexStream(); + fn test_peek_terminated() { + let (port, chan): (Port<int>, Chan<int>) = stream(); - left.send(~"abc"); - right.send(123); + { + // Destroy the channel + let _chan = chan; + } - assert_eq!(left.recv(), 123); - assert_eq!(right.recv(), ~"abc"); + assert!(!port.peek()); } } diff --git a/src/libstd/condition.rs b/src/libstd/condition.rs new file mode 100644 index 00000000000..baa6722b193 --- /dev/null +++ b/src/libstd/condition.rs @@ -0,0 +1,217 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! Condition handling */ + +use prelude::*; +use local_data::{local_data_pop, local_data_set}; + +// helper for transmutation, shown below. +type RustClosure = (int, int); + +pub struct Handler<T, U> { + handle: RustClosure, + prev: Option<@Handler<T, U>>, +} + +pub struct Condition<'self, T, U> { + name: &'static str, + key: local_data::LocalDataKey<'self, Handler<T, U>> +} + +pub impl<'self, T, U> Condition<'self, T, U> { + fn trap(&'self self, h: &'self fn(T) -> U) -> Trap<'self, T, U> { + unsafe { + let p : *RustClosure = ::cast::transmute(&h); + let prev = local_data::local_data_get(self.key); + let h = @Handler { handle: *p, prev: prev }; + Trap { cond: self, handler: h } + } + } + + fn raise(&self, t: T) -> U { + let msg = fmt!("Unhandled condition: %s: %?", self.name, t); + self.raise_default(t, || fail!(copy msg)) + } + + fn raise_default(&self, t: T, default: &fn() -> U) -> U { + unsafe { + match local_data_pop(self.key) { + None => { + debug!("Condition.raise: found no handler"); + default() + } + Some(handler) => { + debug!("Condition.raise: found handler"); + match handler.prev { + None => {} + Some(hp) => local_data_set(self.key, hp) + } + let handle : &fn(T) -> U = + ::cast::transmute(handler.handle); + let u = handle(t); + local_data_set(self.key, handler); + u + } + } + } + } +} + +struct Trap<'self, T, U> { + cond: &'self Condition<'self, T, U>, + handler: @Handler<T, U> +} + +pub impl<'self, T, U> Trap<'self, T, U> { + fn in<V>(&self, inner: &'self fn() -> V) -> V { + unsafe { + let _g = Guard { cond: self.cond }; + debug!("Trap: pushing handler to TLS"); + local_data_set(self.cond.key, self.handler); + inner() + } + } +} + +struct Guard<'self, T, U> { + cond: &'self Condition<'self, T, U> +} + +#[unsafe_destructor] +impl<'self, T, U> Drop for Guard<'self, T, U> { + fn finalize(&self) { + unsafe { + debug!("Guard: popping handler from TLS"); + let curr = local_data_pop(self.cond.key); + match curr { + None => {} + Some(h) => match h.prev { + None => {} + Some(hp) => local_data_set(self.cond.key, hp) + } + } + } + } +} + +#[cfg(test)] +mod test { + condition! { + sadness: int -> int; + } + + fn trouble(i: int) { + debug!("trouble: raising condition"); + let j = sadness::cond.raise(i); + debug!("trouble: handler recovered with %d", j); + } + + fn nested_trap_test_inner() { + let mut inner_trapped = false; + + do sadness::cond.trap(|_j| { + debug!("nested_trap_test_inner: in handler"); + inner_trapped = true; + 0 + }).in { + debug!("nested_trap_test_inner: in protected block"); + trouble(1); + } + + assert!(inner_trapped); + } + + #[test] + fn nested_trap_test_outer() { + let mut outer_trapped = false; + + do sadness::cond.trap(|_j| { + debug!("nested_trap_test_outer: in handler"); + outer_trapped = true; 0 + }).in { + debug!("nested_guard_test_outer: in protected block"); + nested_trap_test_inner(); + trouble(1); + } + + assert!(outer_trapped); + } + + fn nested_reraise_trap_test_inner() { + let mut inner_trapped = false; + + do sadness::cond.trap(|_j| { + debug!("nested_reraise_trap_test_inner: in handler"); + inner_trapped = true; + let i = 10; + debug!("nested_reraise_trap_test_inner: handler re-raising"); + sadness::cond.raise(i) + }).in { + debug!("nested_reraise_trap_test_inner: in protected block"); + trouble(1); + } + + assert!(inner_trapped); + } + + #[test] + fn nested_reraise_trap_test_outer() { + let mut outer_trapped = false; + + do sadness::cond.trap(|_j| { + debug!("nested_reraise_trap_test_outer: in handler"); + outer_trapped = true; 0 + }).in { + debug!("nested_reraise_trap_test_outer: in protected block"); + nested_reraise_trap_test_inner(); + } + + assert!(outer_trapped); + } + + #[test] + fn test_default() { + let mut trapped = false; + + do sadness::cond.trap(|j| { + debug!("test_default: in handler"); + sadness::cond.raise_default(j, || { trapped=true; 5 }) + }).in { + debug!("test_default: in protected block"); + trouble(1); + } + + assert!(trapped); + } + + // Issue #6009 + mod m { + condition! { + sadness: int -> int; + } + + mod n { + use super::sadness; + + #[test] + fn test_conditions_are_public() { + let mut trapped = false; + do sadness::cond.trap(|_| { + trapped = true; + 0 + }).in { + sadness::cond.raise(0); + } + assert!(trapped); + } + } + } +} diff --git a/src/libstd/container.rs b/src/libstd/container.rs new file mode 100644 index 00000000000..505aa5881c5 --- /dev/null +++ b/src/libstd/container.rs @@ -0,0 +1,101 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Container traits + +use option::Option; + +pub trait Container { + /// Return the number of elements in the container + fn len(&const self) -> uint; + + /// Return true if the container contains no elements + fn is_empty(&const self) -> bool; +} + +pub trait Mutable: Container { + /// Clear the container, removing all values. + fn clear(&mut self); +} + +pub trait Map<K, V>: Mutable { + /// Return true if the map contains a value for the specified key + fn contains_key(&self, key: &K) -> bool; + + // Visits all keys and values + fn each<'a>(&'a self, f: &fn(&K, &'a V) -> bool) -> bool; + + /// Visit all keys + fn each_key(&self, f: &fn(&K) -> bool) -> bool; + + /// Visit all values + fn each_value<'a>(&'a self, f: &fn(&'a V) -> bool) -> bool; + + /// Iterate over the map and mutate the contained values + fn mutate_values(&mut self, f: &fn(&K, &mut V) -> bool) -> bool; + + /// Return a reference to the value corresponding to the key + fn find<'a>(&'a self, key: &K) -> Option<&'a V>; + + /// Return a mutable reference to the value corresponding to the key + fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>; + + /// Insert a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Return true if the key did + /// not already exist in the map. + fn insert(&mut self, key: K, value: V) -> bool; + + /// Remove a key-value pair from the map. Return true if the key + /// was present in the map, otherwise false. + fn remove(&mut self, key: &K) -> bool; + + /// Insert a key-value pair from the map. If the key already had a value + /// present in the map, that value is returned. Otherwise None is returned. + fn swap(&mut self, k: K, v: V) -> Option<V>; + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + fn pop(&mut self, k: &K) -> Option<V>; +} + +pub trait Set<T>: Mutable { + /// Return true if the set contains a value + fn contains(&self, value: &T) -> bool; + + /// Add a value to the set. Return true if the value was not already + /// present in the set. + fn insert(&mut self, value: T) -> bool; + + /// Remove a value from the set. Return true if the value was + /// present in the set. + fn remove(&mut self, value: &T) -> bool; + + /// Return true if the set has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + fn is_disjoint(&self, other: &Self) -> bool; + + /// Return true if the set is a subset of another + fn is_subset(&self, other: &Self) -> bool; + + /// Return true if the set is a superset of another + fn is_superset(&self, other: &Self) -> bool; + + /// Visit the values representing the difference + fn difference(&self, other: &Self, f: &fn(&T) -> bool) -> bool; + + /// Visit the values representing the symmetric difference + fn symmetric_difference(&self, other: &Self, f: &fn(&T) -> bool) -> bool; + + /// Visit the values representing the intersection + fn intersection(&self, other: &Self, f: &fn(&T) -> bool) -> bool; + + /// Visit the values representing the union + fn union(&self, other: &Self, f: &fn(&T) -> bool) -> bool; +} diff --git a/src/libstd/core.rc b/src/libstd/core.rc new file mode 100644 index 00000000000..60093ff96bb --- /dev/null +++ b/src/libstd/core.rc @@ -0,0 +1,226 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +# The Rust core library + +The Rust core library provides runtime features required by the language, +including the task scheduler and memory allocators, as well as library +support for Rust built-in types, platform abstractions, and other commonly +used features. + +`core` includes modules corresponding to each of the integer types, each of +the floating point types, the `bool` type, tuples, characters, strings +(`str`), vectors (`vec`), managed boxes (`managed`), owned boxes (`owned`), +and unsafe and borrowed pointers (`ptr`). Additionally, `core` provides +pervasive types (`option` and `result`), task creation and communication +primitives (`task`, `comm`), platform abstractions (`os` and `path`), basic +I/O abstractions (`io`), common traits (`kinds`, `ops`, `cmp`, `num`, +`to_str`), and complete bindings to the C standard library (`libc`). + +# Core injection and the Rust prelude + +`core` is imported at the topmost level of every crate by default, as +if the first line of each crate was + + extern mod core; + +This means that the contents of core can be accessed from from any context +with the `core::` path prefix, as in `use core::vec`, `use core::task::spawn`, +etc. + +Additionally, `core` contains a `prelude` module that reexports many of the +most common core modules, types and traits. The contents of the prelude are +imported into every *module* by default. Implicitly, all modules behave as if +they contained the following prologue: + + use core::prelude::*; + +*/ + + +#[link(name = "core", + vers = "0.7-pre", + uuid = "c70c24a7-5551-4f73-8e37-380b11d80be8", + url = "https://github.com/mozilla/rust/tree/master/src/libcore")]; + +#[comment = "The Rust core library"]; +#[license = "MIT/ASL2"]; +#[crate_type = "lib"]; + + +// Don't link to core. We are core. +#[no_core]; + +#[deny(non_camel_case_types)]; + +// Make core testable by not duplicating lang items. See #2912 +#[cfg(test)] extern mod realcore(name = "core"); +#[cfg(test)] pub use kinds = realcore::kinds; +#[cfg(test)] pub use ops = realcore::ops; +#[cfg(test)] pub use cmp = realcore::cmp; + +// On Linux, link to the runtime with -lrt. +#[cfg(target_os = "linux")] +#[doc(hidden)] +pub mod linkhack { + #[link_args="-lrustrt -lrt"] + #[link_args = "-lpthread"] + extern { + } +} + +// Internal macros +mod macros; + +/* The Prelude. */ + +pub mod prelude; + +/* Primitive types */ + +#[path = "num/int-template.rs"] #[merge = "num/int-template/int.rs"] +pub mod int; +#[path = "num/int-template.rs"] #[merge = "num/int-template/i8.rs"] +pub mod i8; +#[path = "num/int-template.rs"] #[merge = "num/int-template/i16.rs"] +pub mod i16; +#[path = "num/int-template.rs"] #[merge = "num/int-template/i32.rs"] +pub mod i32; +#[path = "num/int-template.rs"] #[merge = "num/int-template/i64.rs"] +pub mod i64; +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/uint.rs"] +pub mod uint; + +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u8.rs"] +pub mod u8; +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u16.rs"] +pub mod u16; +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u32.rs"] +pub mod u32; +#[path = "num/uint-template.rs"] #[merge = "num/uint-template/u64.rs"] +pub mod u64; + +#[path = "num/float.rs"] +pub mod float; +#[path = "num/f32.rs"] +pub mod f32; +#[path = "num/f64.rs"] +pub mod f64; + +pub mod nil; +pub mod bool; +pub mod char; +pub mod tuple; + +pub mod vec; +pub mod at_vec; +pub mod str; + +#[path = "str/ascii.rs"] +pub mod ascii; + +pub mod ptr; +pub mod owned; +pub mod managed; + + +/* Core language traits */ + +#[cfg(not(test))] pub mod kinds; +#[cfg(not(test))] pub mod ops; +#[cfg(not(test))] pub mod cmp; + + +/* Common traits */ + +pub mod from_str; +#[path = "num/num.rs"] +pub mod num; +pub mod iter; +pub mod old_iter; +pub mod iterator; +pub mod to_str; +pub mod to_bytes; +pub mod clone; +pub mod io; +pub mod hash; +pub mod container; + + +/* Common data structures */ + +pub mod option; +pub mod result; +pub mod either; +pub mod hashmap; +pub mod cell; +pub mod trie; + + +/* Tasks and communication */ + +#[path = "task/mod.rs"] +pub mod task; +pub mod comm; +pub mod pipes; +pub mod local_data; + + +/* Runtime and platform support */ + +pub mod gc; +pub mod libc; +pub mod os; +pub mod path; +pub mod rand; +pub mod run; +pub mod sys; +pub mod cast; +pub mod repr; +pub mod cleanup; +pub mod reflect; +pub mod condition; +pub mod logging; +pub mod util; + + +/* Unsupported interfaces */ + +// Private APIs +#[path = "unstable/mod.rs"] +pub mod unstable; + +/* For internal use, not exported */ + +mod unicode; +#[path = "num/cmath.rs"] +mod cmath; +mod stackwalk; + +// XXX: This shouldn't be pub, and it should be reexported under 'unstable' +// but name resolution doesn't work without it being pub. +#[path = "rt/mod.rs"] +pub mod rt; + +// A curious inner-module that's not exported that contains the binding +// 'core' so that macro-expanded references to core::error and such +// can be resolved within libcore. +#[doc(hidden)] +mod core { + pub use clone; + pub use cmp; + pub use condition; + pub use option; + pub use kinds; + pub use sys; + pub use pipes; +} diff --git a/src/libstd/dbg.rs b/src/libstd/dbg.rs deleted file mode 100644 index 4b2d2a60a68..00000000000 --- a/src/libstd/dbg.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Unsafe debugging functions for inspecting values. - -use core::cast::transmute; -use core::sys; - -pub mod rustrt { - use core::sys; - - #[abi = "cdecl"] - pub extern { - pub unsafe fn debug_tydesc(td: *sys::TypeDesc); - pub unsafe fn debug_opaque(td: *sys::TypeDesc, x: *()); - pub unsafe fn debug_box(td: *sys::TypeDesc, x: *()); - pub unsafe fn debug_tag(td: *sys::TypeDesc, x: *()); - pub unsafe fn debug_fn(td: *sys::TypeDesc, x: *()); - pub unsafe fn debug_ptrcast(td: *sys::TypeDesc, x: *()) -> *(); - pub unsafe fn rust_dbg_breakpoint(); - } -} - -pub fn debug_tydesc<T>() { - unsafe { - rustrt::debug_tydesc(sys::get_type_desc::<T>()); - } -} - -pub fn debug_opaque<T>(x: T) { - unsafe { - rustrt::debug_opaque(sys::get_type_desc::<T>(), transmute(&x)); - } -} - -pub fn debug_box<T>(x: @T) { - unsafe { - rustrt::debug_box(sys::get_type_desc::<T>(), transmute(&x)); - } -} - -pub fn debug_tag<T>(x: T) { - unsafe { - rustrt::debug_tag(sys::get_type_desc::<T>(), transmute(&x)); - } -} - -pub fn debug_fn<T>(x: T) { - unsafe { - rustrt::debug_fn(sys::get_type_desc::<T>(), transmute(&x)); - } -} - -pub unsafe fn ptr_cast<T, U>(x: @T) -> @U { - transmute( - rustrt::debug_ptrcast(sys::get_type_desc::<T>(), transmute(x))) -} - -/// Triggers a debugger breakpoint -pub fn breakpoint() { - unsafe { - rustrt::rust_dbg_breakpoint(); - } -} - -#[test] -fn test_breakpoint_should_not_abort_process_when_not_under_gdb() { - // Triggering a breakpoint involves raising SIGTRAP, which terminates - // the process under normal circumstances - breakpoint(); -} diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs deleted file mode 100644 index 60f16074177..00000000000 --- a/src/libstd/deque.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! A double-ended queue implemented as a circular buffer - -use core::util::replace; - -static initial_capacity: uint = 32u; // 2^5 - -pub struct Deque<T> { - priv nelts: uint, - priv lo: uint, - priv hi: uint, - priv elts: ~[Option<T>] -} - -impl<T> Container for Deque<T> { - /// Return the number of elements in the deque - fn len(&const self) -> uint { self.nelts } - - /// Return true if the deque contains no elements - fn is_empty(&const self) -> bool { self.len() == 0 } -} - -impl<T> Mutable for Deque<T> { - /// Clear the deque, removing all values. - fn clear(&mut self) { - for self.elts.each_mut |x| { *x = None } - self.nelts = 0; - self.lo = 0; - self.hi = 0; - } -} - -pub impl<T> Deque<T> { - /// Create an empty Deque - fn new() -> Deque<T> { - Deque{nelts: 0, lo: 0, hi: 0, - elts: vec::from_fn(initial_capacity, |_| None)} - } - - /// Return a reference to the first element in the deque - /// - /// Fails if the deque is empty - fn peek_front<'a>(&'a self) -> &'a T { get(self.elts, self.lo) } - - /// Return a reference to the last element in the deque - /// - /// Fails if the deque is empty - fn peek_back<'a>(&'a self) -> &'a T { get(self.elts, self.hi - 1u) } - - /// Retrieve an element in the deque by index - /// - /// Fails if there is no element with the given index - fn get<'a>(&'a self, i: int) -> &'a T { - let idx = (self.lo + (i as uint)) % self.elts.len(); - get(self.elts, idx) - } - - /// Iterate over the elements in the deque - fn each(&self, f: &fn(&T) -> bool) -> bool { - self.eachi(|_i, e| f(e)) - } - - /// Iterate over the elements in the deque by index - fn eachi(&self, f: &fn(uint, &T) -> bool) -> bool { - uint::range(0, self.nelts, |i| f(i, self.get(i as int))) - } - - /// Remove and return the first element in the deque - /// - /// Fails if the deque is empty - fn pop_front(&mut self) -> T { - let result = self.elts[self.lo].swap_unwrap(); - self.lo = (self.lo + 1u) % self.elts.len(); - self.nelts -= 1u; - result - } - - /// Remove and return the last element in the deque - /// - /// Fails if the deque is empty - fn pop_back(&mut self) -> T { - if self.hi == 0u { - self.hi = self.elts.len() - 1u; - } else { self.hi -= 1u; } - let result = self.elts[self.hi].swap_unwrap(); - self.elts[self.hi] = None; - self.nelts -= 1u; - result - } - - /// Prepend an element to the deque - fn add_front(&mut self, t: T) { - let oldlo = self.lo; - if self.lo == 0u { - self.lo = self.elts.len() - 1u; - } else { self.lo -= 1u; } - if self.lo == self.hi { - self.elts = grow(self.nelts, oldlo, self.elts); - self.lo = self.elts.len() - 1u; - self.hi = self.nelts; - } - self.elts[self.lo] = Some(t); - self.nelts += 1u; - } - - /// Append an element to the deque - fn add_back(&mut self, t: T) { - if self.lo == self.hi && self.nelts != 0u { - self.elts = grow(self.nelts, self.lo, self.elts); - self.lo = 0u; - self.hi = self.nelts; - } - self.elts[self.hi] = Some(t); - self.hi = (self.hi + 1u) % self.elts.len(); - self.nelts += 1u; - } -} - -/// Grow is only called on full elts, so nelts is also len(elts), unlike -/// elsewhere. -fn grow<T>(nelts: uint, lo: uint, elts: &mut [Option<T>]) -> ~[Option<T>] { - assert_eq!(nelts, elts.len()); - let mut rv = ~[]; - - do rv.grow_fn(nelts + 1) |i| { - replace(&mut elts[(lo + i) % nelts], None) - } - - rv -} - -fn get<'r, T>(elts: &'r [Option<T>], i: uint) -> &'r T { - match elts[i] { Some(ref t) => t, _ => fail!() } -} - -#[cfg(test)] -mod tests { - use super::*; - use core::cmp::Eq; - use core::kinds::Copy; - - #[test] - fn test_simple() { - let mut d = Deque::new(); - assert_eq!(d.len(), 0u); - d.add_front(17); - d.add_front(42); - d.add_back(137); - assert_eq!(d.len(), 3u); - d.add_back(137); - assert_eq!(d.len(), 4u); - debug!(d.peek_front()); - assert_eq!(*d.peek_front(), 42); - debug!(d.peek_back()); - assert_eq!(*d.peek_back(), 137); - let mut i: int = d.pop_front(); - debug!(i); - assert_eq!(i, 42); - i = d.pop_back(); - debug!(i); - assert_eq!(i, 137); - i = d.pop_back(); - debug!(i); - assert_eq!(i, 137); - i = d.pop_back(); - debug!(i); - assert_eq!(i, 17); - assert_eq!(d.len(), 0u); - d.add_back(3); - assert_eq!(d.len(), 1u); - d.add_front(2); - assert_eq!(d.len(), 2u); - d.add_back(4); - assert_eq!(d.len(), 3u); - d.add_front(1); - assert_eq!(d.len(), 4u); - debug!(d.get(0)); - debug!(d.get(1)); - debug!(d.get(2)); - debug!(d.get(3)); - assert_eq!(*d.get(0), 1); - assert_eq!(*d.get(1), 2); - assert_eq!(*d.get(2), 3); - assert_eq!(*d.get(3), 4); - } - - #[test] - fn test_boxes() { - let a: @int = @5; - let b: @int = @72; - let c: @int = @64; - let d: @int = @175; - - let mut deq = Deque::new(); - assert_eq!(deq.len(), 0); - deq.add_front(a); - deq.add_front(b); - deq.add_back(c); - assert_eq!(deq.len(), 3); - deq.add_back(d); - assert_eq!(deq.len(), 4); - assert_eq!(*deq.peek_front(), b); - assert_eq!(*deq.peek_back(), d); - assert_eq!(deq.pop_front(), b); - assert_eq!(deq.pop_back(), d); - assert_eq!(deq.pop_back(), c); - assert_eq!(deq.pop_back(), a); - assert_eq!(deq.len(), 0); - deq.add_back(c); - assert_eq!(deq.len(), 1); - deq.add_front(b); - assert_eq!(deq.len(), 2); - deq.add_back(d); - assert_eq!(deq.len(), 3); - deq.add_front(a); - assert_eq!(deq.len(), 4); - assert_eq!(*deq.get(0), a); - assert_eq!(*deq.get(1), b); - assert_eq!(*deq.get(2), c); - assert_eq!(*deq.get(3), d); - } - - #[cfg(test)] - fn test_parameterized<T:Copy + Eq>(a: T, b: T, c: T, d: T) { - let mut deq = Deque::new(); - assert_eq!(deq.len(), 0); - deq.add_front(a); - deq.add_front(b); - deq.add_back(c); - assert_eq!(deq.len(), 3); - deq.add_back(d); - assert_eq!(deq.len(), 4); - assert_eq!(*deq.peek_front(), b); - assert_eq!(*deq.peek_back(), d); - assert_eq!(deq.pop_front(), b); - assert_eq!(deq.pop_back(), d); - assert_eq!(deq.pop_back(), c); - assert_eq!(deq.pop_back(), a); - assert_eq!(deq.len(), 0); - deq.add_back(c); - assert_eq!(deq.len(), 1); - deq.add_front(b); - assert_eq!(deq.len(), 2); - deq.add_back(d); - assert_eq!(deq.len(), 3); - deq.add_front(a); - assert_eq!(deq.len(), 4); - assert_eq!(*deq.get(0), a); - assert_eq!(*deq.get(1), b); - assert_eq!(*deq.get(2), c); - assert_eq!(*deq.get(3), d); - } - - #[deriving(Eq)] - enum Taggy { One(int), Two(int, int), Three(int, int, int), } - - #[deriving(Eq)] - enum Taggypar<T> { - Onepar(int), Twopar(int, int), Threepar(int, int, int), - } - - #[deriving(Eq)] - struct RecCy { - x: int, - y: int, - t: Taggy - } - - #[test] - fn test_param_int() { - test_parameterized::<int>(5, 72, 64, 175); - } - - #[test] - fn test_param_at_int() { - test_parameterized::<@int>(@5, @72, @64, @175); - } - - #[test] - fn test_param_taggy() { - test_parameterized::<Taggy>(One(1), Two(1, 2), Three(1, 2, 3), - Two(17, 42)); - } - - #[test] - fn test_param_taggypar() { - test_parameterized::<Taggypar<int>>(Onepar::<int>(1), - Twopar::<int>(1, 2), - Threepar::<int>(1, 2, 3), - Twopar::<int>(17, 42)); - } - - #[test] - fn test_param_reccy() { - let reccy1 = RecCy { x: 1, y: 2, t: One(1) }; - let reccy2 = RecCy { x: 345, y: 2, t: Two(1, 2) }; - let reccy3 = RecCy { x: 1, y: 777, t: Three(1, 2, 3) }; - let reccy4 = RecCy { x: 19, y: 252, t: Two(17, 42) }; - test_parameterized::<RecCy>(reccy1, reccy2, reccy3, reccy4); - } - - #[test] - fn test_eachi() { - let mut deq = Deque::new(); - deq.add_back(1); - deq.add_back(2); - deq.add_back(3); - - for deq.eachi |i, e| { - assert_eq!(*e, i + 1); - } - - deq.pop_front(); - - for deq.eachi |i, e| { - assert_eq!(*e, i + 2); - } - - } -} diff --git a/src/libstd/dlist.rs b/src/libstd/dlist.rs deleted file mode 100644 index 57cd03e16a0..00000000000 --- a/src/libstd/dlist.rs +++ /dev/null @@ -1,981 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - -A doubly-linked list. Supports O(1) head, tail, count, push, pop, etc. - -# Safety note - -Do not use ==, !=, <, etc on doubly-linked lists -- it may not terminate. - -*/ - -use core::managed; - -pub type DListLink<T> = Option<@mut DListNode<T>>; - -pub struct DListNode<T> { - data: T, - linked: bool, // for assertions - prev: DListLink<T>, - next: DListLink<T>, -} - -pub struct DList<T> { - size: uint, - hd: DListLink<T>, - tl: DListLink<T>, -} - -priv impl<T> DListNode<T> { - fn assert_links(@mut self) { - match self.next { - Some(neighbour) => match neighbour.prev { - Some(me) => if !managed::mut_ptr_eq(self, me) { - fail!("Asymmetric next-link in dlist node.") - }, - None => fail!("One-way next-link in dlist node.") - }, - None => () - } - match self.prev { - Some(neighbour) => match neighbour.next { - Some(me) => if !managed::mut_ptr_eq(me, self) { - fail!("Asymmetric prev-link in dlist node.") - }, - None => fail!("One-way prev-link in dlist node.") - }, - None => () - } - } -} - -pub impl<T> DListNode<T> { - /// Get the next node in the list, if there is one. - fn next_link(@mut self) -> DListLink<T> { - self.assert_links(); - self.next - } - /// Get the next node in the list, failing if there isn't one. - fn next_node(@mut self) -> @mut DListNode<T> { - match self.next_link() { - Some(nobe) => nobe, - None => fail!("This dlist node has no next neighbour.") - } - } - /// Get the previous node in the list, if there is one. - fn prev_link(@mut self) -> DListLink<T> { - self.assert_links(); - self.prev - } - /// Get the previous node in the list, failing if there isn't one. - fn prev_node(@mut self) -> @mut DListNode<T> { - match self.prev_link() { - Some(nobe) => nobe, - None => fail!("This dlist node has no previous neighbour.") - } - } -} - -/// Creates a new dlist node with the given data. -pub fn new_dlist_node<T>(data: T) -> @mut DListNode<T> { - @mut DListNode { data: data, linked: false, prev: None, next: None } -} - -/// Creates a new, empty dlist. -pub fn DList<T>() -> @mut DList<T> { - @mut DList { size: 0, hd: None, tl: None } -} - -/// Creates a new dlist with a single element -pub fn from_elem<T>(data: T) -> @mut DList<T> { - let list = DList(); - list.push(data); - list -} - -pub fn from_vec<T:Copy>(vec: &[T]) -> @mut DList<T> { - do vec::foldl(DList(), vec) |list,data| { - list.push(*data); // Iterating left-to-right -- add newly to the tail. - list - } -} - -/// Produce a list from a list of lists, leaving no elements behind in the -/// input. O(number of sub-lists). -pub fn concat<T>(lists: @mut DList<@mut DList<T>>) -> @mut DList<T> { - let result = DList(); - while !lists.is_empty() { - result.append(lists.pop().get()); - } - result -} - -priv impl<T> DList<T> { - fn new_link(data: T) -> DListLink<T> { - Some(@mut DListNode { - data: data, - linked: true, - prev: None, - next: None - }) - } - fn assert_mine(@mut self, nobe: @mut DListNode<T>) { - // These asserts could be stronger if we had node-root back-pointers, - // but those wouldn't allow for O(1) append. - if self.size == 0 { - fail!("This dlist is empty; that node can't be on it.") - } - if !nobe.linked { fail!("That node isn't linked to any dlist.") } - if !((nobe.prev.is_some() - || managed::mut_ptr_eq(self.hd.expect("headless dlist?"), - nobe)) && - (nobe.next.is_some() - || managed::mut_ptr_eq(self.tl.expect("tailless dlist?"), - nobe))) { - fail!("That node isn't on this dlist.") - } - } - fn make_mine(&self, nobe: @mut DListNode<T>) { - if nobe.prev.is_some() || nobe.next.is_some() || nobe.linked { - fail!("Cannot insert node that's already on a dlist!") - } - nobe.linked = true; - } - // Link two nodes together. If either of them are 'none', also sets - // the head and/or tail pointers appropriately. - #[inline(always)] - fn link(&mut self, before: DListLink<T>, after: DListLink<T>) { - match before { - Some(neighbour) => neighbour.next = after, - None => self.hd = after - } - match after { - Some(neighbour) => neighbour.prev = before, - None => self.tl = before - } - } - // Remove a node from the list. - fn unlink(@mut self, nobe: @mut DListNode<T>) { - self.assert_mine(nobe); - assert!(self.size > 0); - self.link(nobe.prev, nobe.next); - nobe.prev = None; // Release extraneous references. - nobe.next = None; - nobe.linked = false; - self.size -= 1; - } - - fn add_head(@mut self, nobe: DListLink<T>) { - self.link(nobe, self.hd); // Might set tail too. - self.hd = nobe; - self.size += 1; - } - fn add_tail(@mut self, nobe: DListLink<T>) { - self.link(self.tl, nobe); // Might set head too. - self.tl = nobe; - self.size += 1; - } - fn insert_left(@mut self, - nobe: DListLink<T>, - neighbour: @mut DListNode<T>) { - self.assert_mine(neighbour); - assert!(self.size > 0); - self.link(neighbour.prev, nobe); - self.link(nobe, Some(neighbour)); - self.size += 1; - } - fn insert_right(@mut self, - neighbour: @mut DListNode<T>, - nobe: DListLink<T>) { - self.assert_mine(neighbour); - assert!(self.size > 0); - self.link(nobe, neighbour.next); - self.link(Some(neighbour), nobe); - self.size += 1; - } -} - -pub impl<T> DList<T> { - /// Get the size of the list. O(1). - fn len(@mut self) -> uint { self.size } - /// Returns true if the list is empty. O(1). - fn is_empty(@mut self) -> bool { self.len() == 0 } - - /// Add data to the head of the list. O(1). - fn push_head(@mut self, data: T) { - self.add_head(DList::new_link(data)); - } - /** - * Add data to the head of the list, and get the new containing - * node. O(1). - */ - fn push_head_n(@mut self, data: T) -> @mut DListNode<T> { - let nobe = DList::new_link(data); - self.add_head(nobe); - nobe.get() - } - /// Add data to the tail of the list. O(1). - fn push(@mut self, data: T) { - self.add_tail(DList::new_link(data)); - } - /** - * Add data to the tail of the list, and get the new containing - * node. O(1). - */ - fn push_n(@mut self, data: T) -> @mut DListNode<T> { - let nobe = DList::new_link(data); - self.add_tail(nobe); - nobe.get() - } - /** - * Insert data into the middle of the list, left of the given node. - * O(1). - */ - fn insert_before(@mut self, data: T, neighbour: @mut DListNode<T>) { - self.insert_left(DList::new_link(data), neighbour); - } - /** - * Insert an existing node in the middle of the list, left of the - * given node. O(1). - */ - fn insert_n_before(@mut self, - nobe: @mut DListNode<T>, - neighbour: @mut DListNode<T>) { - self.make_mine(nobe); - self.insert_left(Some(nobe), neighbour); - } - /** - * Insert data in the middle of the list, left of the given node, - * and get its containing node. O(1). - */ - fn insert_before_n( - @mut self, - data: T, - neighbour: @mut DListNode<T> - ) -> @mut DListNode<T> { - let nobe = DList::new_link(data); - self.insert_left(nobe, neighbour); - nobe.get() - } - /** - * Insert data into the middle of the list, right of the given node. - * O(1). - */ - fn insert_after(@mut self, data: T, neighbour: @mut DListNode<T>) { - self.insert_right(neighbour, DList::new_link(data)); - } - /** - * Insert an existing node in the middle of the list, right of the - * given node. O(1). - */ - fn insert_n_after(@mut self, - nobe: @mut DListNode<T>, - neighbour: @mut DListNode<T>) { - self.make_mine(nobe); - self.insert_right(neighbour, Some(nobe)); - } - /** - * Insert data in the middle of the list, right of the given node, - * and get its containing node. O(1). - */ - fn insert_after_n( - @mut self, - data: T, - neighbour: @mut DListNode<T> - ) -> @mut DListNode<T> { - let nobe = DList::new_link(data); - self.insert_right(neighbour, nobe); - nobe.get() - } - - /// Remove a node from the head of the list. O(1). - fn pop_n(@mut self) -> DListLink<T> { - let hd = self.peek_n(); - hd.map(|nobe| self.unlink(*nobe)); - hd - } - /// Remove a node from the tail of the list. O(1). - fn pop_tail_n(@mut self) -> DListLink<T> { - let tl = self.peek_tail_n(); - tl.map(|nobe| self.unlink(*nobe)); - tl - } - /// Get the node at the list's head. O(1). - fn peek_n(@mut self) -> DListLink<T> { self.hd } - /// Get the node at the list's tail. O(1). - fn peek_tail_n(@mut self) -> DListLink<T> { self.tl } - - /// Get the node at the list's head, failing if empty. O(1). - fn head_n(@mut self) -> @mut DListNode<T> { - match self.hd { - Some(nobe) => nobe, - None => fail!("Attempted to get the head of an empty dlist.") - } - } - /// Get the node at the list's tail, failing if empty. O(1). - fn tail_n(@mut self) -> @mut DListNode<T> { - match self.tl { - Some(nobe) => nobe, - None => fail!("Attempted to get the tail of an empty dlist.") - } - } - - /// Remove a node from anywhere in the list. O(1). - fn remove(@mut self, nobe: @mut DListNode<T>) { self.unlink(nobe); } - - /** - * Empty another list onto the end of this list, joining this list's tail - * to the other list's head. O(1). - */ - fn append(@mut self, them: @mut DList<T>) { - if managed::mut_ptr_eq(self, them) { - fail!("Cannot append a dlist to itself!") - } - if them.len() > 0 { - self.link(self.tl, them.hd); - self.tl = them.tl; - self.size += them.size; - them.size = 0; - them.hd = None; - them.tl = None; - } - } - /** - * Empty another list onto the start of this list, joining the other - * list's tail to this list's head. O(1). - */ - fn prepend(@mut self, them: @mut DList<T>) { - if managed::mut_ptr_eq(self, them) { - fail!("Cannot prepend a dlist to itself!") - } - if them.len() > 0 { - self.link(them.tl, self.hd); - self.hd = them.hd; - self.size += them.size; - them.size = 0; - them.hd = None; - them.tl = None; - } - } - - /// Reverse the list's elements in place. O(n). - fn reverse(@mut self) { - do self.hd.while_some |nobe| { - let next_nobe = nobe.next; - self.remove(nobe); - self.make_mine(nobe); - self.add_head(Some(nobe)); - next_nobe - } - } - - /** - * Remove everything from the list. This is important because the cyclic - * links won't otherwise be automatically refcounted-collected. O(n). - */ - fn clear(@mut self) { - // Cute as it would be to simply detach the list and proclaim "O(1)!", - // the GC would still be a hidden O(n). Better to be honest about it. - while !self.is_empty() { - let _ = self.pop_n(); - } - } - - /// Iterate over nodes. - fn each_node(@mut self, f: &fn(@mut DListNode<T>) -> bool) -> bool { - let mut link = self.peek_n(); - while link.is_some() { - let nobe = link.get(); - if !f(nobe) { return false; } - link = nobe.next_link(); - } - return true; - } - - /// Check data structure integrity. O(n). - fn assert_consistent(@mut self) { - if self.hd.is_none() || self.tl.is_none() { - assert!(self.hd.is_none() && self.tl.is_none()); - } - // iterate forwards - let mut count = 0; - let mut link = self.peek_n(); - let mut rabbit = link; - while link.is_some() { - let nobe = link.get(); - assert!(nobe.linked); - // check cycle - if rabbit.is_some() { - rabbit = rabbit.get().next; - } - if rabbit.is_some() { - rabbit = rabbit.get().next; - } - if rabbit.is_some() { - assert!(!managed::mut_ptr_eq(rabbit.get(), nobe)); - } - // advance - link = nobe.next_link(); - count += 1; - } - assert_eq!(count, self.len()); - // iterate backwards - some of this is probably redundant. - link = self.peek_tail_n(); - rabbit = link; - while link.is_some() { - let nobe = link.get(); - assert!(nobe.linked); - // check cycle - if rabbit.is_some() { - rabbit = rabbit.get().prev; - } - if rabbit.is_some() { - rabbit = rabbit.get().prev; - } - if rabbit.is_some() { - assert!(!managed::mut_ptr_eq(rabbit.get(), nobe)); - } - // advance - link = nobe.prev_link(); - count -= 1; - } - assert_eq!(count, 0); - } -} - -pub impl<T:Copy> DList<T> { - /// Remove data from the head of the list. O(1). - fn pop(@mut self) -> Option<T> { - self.pop_n().map(|nobe| nobe.data) - } - - /// Remove data from the tail of the list. O(1). - fn pop_tail(@mut self) -> Option<T> { - self.pop_tail_n().map(|nobe| nobe.data) - } - - /// Get data at the list's head. O(1). - fn peek(@mut self) -> Option<T> { - self.peek_n().map(|nobe| nobe.data) - } - - /// Get data at the list's tail. O(1). - fn peek_tail(@mut self) -> Option<T> { - self.peek_tail_n().map (|nobe| nobe.data) - } - - /// Get data at the list's head, failing if empty. O(1). - fn head(@mut self) -> T { self.head_n().data } - - /// Get data at the list's tail, failing if empty. O(1). - fn tail(@mut self) -> T { self.tail_n().data } - - /// Get the elements of the list as a vector. O(n). - fn to_vec(@mut self) -> ~[T] { - let mut v = vec::with_capacity(self.size); - for old_iter::eachi(&self) |index,data| { - v[index] = *data; - } - v - } -} - -impl<T> BaseIter<T> for @mut DList<T> { - /** - * Iterates through the current contents. - * - * Attempts to access this dlist during iteration are allowed (to - * allow for e.g. breadth-first search with in-place enqueues), but - * removing the current node is forbidden. - */ - fn each(&self, f: &fn(v: &T) -> bool) -> bool { - let mut link = self.peek_n(); - while link.is_some() { - let nobe = link.get(); - assert!(nobe.linked); - - { - let frozen_nobe = &*nobe; - if !f(&frozen_nobe.data) { return false; } - } - - // Check (weakly) that the user didn't do a remove. - if self.size == 0 { - fail!("The dlist became empty during iteration??") - } - if !nobe.linked || - (!((nobe.prev.is_some() - || managed::mut_ptr_eq(self.hd.expect("headless dlist?"), - nobe)) - && (nobe.next.is_some() - || managed::mut_ptr_eq(self.tl.expect("tailless dlist?"), - nobe)))) { - fail!("Removing a dlist node during iteration is forbidden!") - } - link = nobe.next_link(); - } - return true; - } - - #[inline(always)] - fn size_hint(&self) -> Option<uint> { Some(self.len()) } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_dlist_concat() { - let a = from_vec(~[1,2]); - let b = from_vec(~[3,4]); - let c = from_vec(~[5,6]); - let d = from_vec(~[7,8]); - let ab = from_vec(~[a,b]); - let cd = from_vec(~[c,d]); - let abcd = concat(concat(from_vec(~[ab,cd]))); - abcd.assert_consistent(); assert_eq!(abcd.len(), 8); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 1); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 2); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 3); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 4); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 5); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 6); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 7); - abcd.assert_consistent(); assert_eq!(abcd.pop().get(), 8); - abcd.assert_consistent(); assert!(abcd.is_empty()); - } - #[test] - fn test_dlist_append() { - let a = from_vec(~[1,2,3]); - let b = from_vec(~[4,5,6]); - a.append(b); - assert_eq!(a.len(), 6); - assert_eq!(b.len(), 0); - b.assert_consistent(); - a.assert_consistent(); assert_eq!(a.pop().get(), 1); - a.assert_consistent(); assert_eq!(a.pop().get(), 2); - a.assert_consistent(); assert_eq!(a.pop().get(), 3); - a.assert_consistent(); assert_eq!(a.pop().get(), 4); - a.assert_consistent(); assert_eq!(a.pop().get(), 5); - a.assert_consistent(); assert_eq!(a.pop().get(), 6); - a.assert_consistent(); assert!(a.is_empty()); - } - #[test] - fn test_dlist_append_empty() { - let a = from_vec(~[1,2,3]); - let b = DList::<int>(); - a.append(b); - assert_eq!(a.len(), 3); - assert_eq!(b.len(), 0); - b.assert_consistent(); - a.assert_consistent(); assert_eq!(a.pop().get(), 1); - a.assert_consistent(); assert_eq!(a.pop().get(), 2); - a.assert_consistent(); assert_eq!(a.pop().get(), 3); - a.assert_consistent(); assert!(a.is_empty()); - } - #[test] - fn test_dlist_append_to_empty() { - let a = DList::<int>(); - let b = from_vec(~[4,5,6]); - a.append(b); - assert_eq!(a.len(), 3); - assert_eq!(b.len(), 0); - b.assert_consistent(); - a.assert_consistent(); assert_eq!(a.pop().get(), 4); - a.assert_consistent(); assert_eq!(a.pop().get(), 5); - a.assert_consistent(); assert_eq!(a.pop().get(), 6); - a.assert_consistent(); assert!(a.is_empty()); - } - #[test] - fn test_dlist_append_two_empty() { - let a = DList::<int>(); - let b = DList::<int>(); - a.append(b); - assert_eq!(a.len(), 0); - assert_eq!(b.len(), 0); - b.assert_consistent(); - a.assert_consistent(); - } - #[test] - #[ignore(cfg(windows))] - #[should_fail] - fn test_dlist_append_self() { - let a = DList::<int>(); - a.append(a); - } - #[test] - #[ignore(cfg(windows))] - #[should_fail] - fn test_dlist_prepend_self() { - let a = DList::<int>(); - a.prepend(a); - } - #[test] - fn test_dlist_prepend() { - let a = from_vec(~[1,2,3]); - let b = from_vec(~[4,5,6]); - b.prepend(a); - assert_eq!(a.len(), 0); - assert_eq!(b.len(), 6); - a.assert_consistent(); - b.assert_consistent(); assert_eq!(b.pop().get(), 1); - b.assert_consistent(); assert_eq!(b.pop().get(), 2); - b.assert_consistent(); assert_eq!(b.pop().get(), 3); - b.assert_consistent(); assert_eq!(b.pop().get(), 4); - b.assert_consistent(); assert_eq!(b.pop().get(), 5); - b.assert_consistent(); assert_eq!(b.pop().get(), 6); - b.assert_consistent(); assert!(b.is_empty()); - } - #[test] - fn test_dlist_reverse() { - let a = from_vec(~[5,4,3,2,1]); - a.reverse(); - assert_eq!(a.len(), 5); - a.assert_consistent(); assert_eq!(a.pop().get(), 1); - a.assert_consistent(); assert_eq!(a.pop().get(), 2); - a.assert_consistent(); assert_eq!(a.pop().get(), 3); - a.assert_consistent(); assert_eq!(a.pop().get(), 4); - a.assert_consistent(); assert_eq!(a.pop().get(), 5); - a.assert_consistent(); assert!(a.is_empty()); - } - #[test] - fn test_dlist_reverse_empty() { - let a = DList::<int>(); - a.reverse(); - assert_eq!(a.len(), 0); - a.assert_consistent(); - } - #[test] - fn test_dlist_each_node() { - let a = from_vec(~[1,2,4,5]); - for a.each_node |nobe| { - if nobe.data > 3 { - a.insert_before(3, nobe); - } - } - assert_eq!(a.len(), 6); - a.assert_consistent(); assert_eq!(a.pop().get(), 1); - a.assert_consistent(); assert_eq!(a.pop().get(), 2); - a.assert_consistent(); assert_eq!(a.pop().get(), 3); - a.assert_consistent(); assert_eq!(a.pop().get(), 4); - a.assert_consistent(); assert_eq!(a.pop().get(), 3); - a.assert_consistent(); assert_eq!(a.pop().get(), 5); - a.assert_consistent(); assert!(a.is_empty()); - } - #[test] - fn test_dlist_clear() { - let a = from_vec(~[5,4,3,2,1]); - a.clear(); - assert_eq!(a.len(), 0); - a.assert_consistent(); - } - #[test] - fn test_dlist_is_empty() { - let empty = DList::<int>(); - let full1 = from_vec(~[1,2,3]); - assert!(empty.is_empty()); - assert!(!full1.is_empty()); - } - #[test] - fn test_dlist_head_tail() { - let l = from_vec(~[1,2,3]); - assert_eq!(l.head(), 1); - assert_eq!(l.tail(), 3); - assert_eq!(l.len(), 3); - } - #[test] - fn test_dlist_pop() { - let l = from_vec(~[1,2,3]); - assert_eq!(l.pop().get(), 1); - assert_eq!(l.tail(), 3); - assert_eq!(l.head(), 2); - assert_eq!(l.pop().get(), 2); - assert_eq!(l.tail(), 3); - assert_eq!(l.head(), 3); - assert_eq!(l.pop().get(), 3); - assert!(l.is_empty()); - assert!(l.pop().is_none()); - } - #[test] - fn test_dlist_pop_tail() { - let l = from_vec(~[1,2,3]); - assert_eq!(l.pop_tail().get(), 3); - assert_eq!(l.tail(), 2); - assert_eq!(l.head(), 1); - assert_eq!(l.pop_tail().get(), 2); - assert_eq!(l.tail(), 1); - assert_eq!(l.head(), 1); - assert_eq!(l.pop_tail().get(), 1); - assert!(l.is_empty()); - assert!(l.pop_tail().is_none()); - } - #[test] - fn test_dlist_push() { - let l = DList::<int>(); - l.push(1); - assert_eq!(l.head(), 1); - assert_eq!(l.tail(), 1); - l.push(2); - assert_eq!(l.head(), 1); - assert_eq!(l.tail(), 2); - l.push(3); - assert_eq!(l.head(), 1); - assert_eq!(l.tail(), 3); - assert_eq!(l.len(), 3); - } - #[test] - fn test_dlist_push_head() { - let l = DList::<int>(); - l.push_head(3); - assert_eq!(l.head(), 3); - assert_eq!(l.tail(), 3); - l.push_head(2); - assert_eq!(l.head(), 2); - assert_eq!(l.tail(), 3); - l.push_head(1); - assert_eq!(l.head(), 1); - assert_eq!(l.tail(), 3); - assert_eq!(l.len(), 3); - } - #[test] - fn test_dlist_foldl() { - let l = from_vec(vec::from_fn(101, |x|x)); - assert_eq!(old_iter::foldl(&l, 0, |accum,elem| *accum+*elem), 5050); - } - #[test] - fn test_dlist_break_early() { - let l = from_vec(~[1,2,3,4,5]); - let mut x = 0; - for l.each |i| { - x += 1; - if (*i == 3) { break; } - } - assert_eq!(x, 3); - } - #[test] - fn test_dlist_remove_head() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let _two = l.push_n(2); - l.assert_consistent(); let _three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(one); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); assert_eq!(l.head(), 2); - l.assert_consistent(); assert_eq!(l.tail(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_mid() { - let l = DList::<int>(); - l.assert_consistent(); let _one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); let _three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(two); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_tail() { - let l = DList::<int>(); - l.assert_consistent(); let _one = l.push_n(1); - l.assert_consistent(); let _two = l.push_n(2); - l.assert_consistent(); let three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(three); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_one_two() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); let _three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(one); - l.assert_consistent(); l.remove(two); - // and through and through, the vorpal blade went snicker-snack - l.assert_consistent(); assert_eq!(l.len(), 1); - l.assert_consistent(); assert_eq!(l.head(), 3); - l.assert_consistent(); assert_eq!(l.tail(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_one_three() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let _two = l.push_n(2); - l.assert_consistent(); let three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(one); - l.assert_consistent(); l.remove(three); - l.assert_consistent(); assert_eq!(l.len(), 1); - l.assert_consistent(); assert_eq!(l.head(), 2); - l.assert_consistent(); assert_eq!(l.tail(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_two_three() { - let l = DList::<int>(); - l.assert_consistent(); let _one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); let three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(two); - l.assert_consistent(); l.remove(three); - l.assert_consistent(); assert_eq!(l.len(), 1); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_remove_all() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); let three = l.push_n(3); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); l.remove(two); - l.assert_consistent(); l.remove(three); - l.assert_consistent(); l.remove(one); // Twenty-three is number one! - l.assert_consistent(); assert!(l.peek().is_none()); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_insert_n_before() { - let l = DList::<int>(); - l.assert_consistent(); let _one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); let three = new_dlist_node(3); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); l.insert_n_before(three, two); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_insert_n_after() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let _two = l.push_n(2); - l.assert_consistent(); let three = new_dlist_node(3); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); l.insert_n_after(three, one); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_insert_before_head() { - let l = DList::<int>(); - l.assert_consistent(); let one = l.push_n(1); - l.assert_consistent(); let _two = l.push_n(2); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); l.insert_before(3, one); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); assert_eq!(l.head(), 3); - l.assert_consistent(); assert_eq!(l.tail(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] - fn test_dlist_insert_after_tail() { - let l = DList::<int>(); - l.assert_consistent(); let _one = l.push_n(1); - l.assert_consistent(); let two = l.push_n(2); - l.assert_consistent(); assert_eq!(l.len(), 2); - l.assert_consistent(); l.insert_after(3, two); - l.assert_consistent(); assert_eq!(l.len(), 3); - l.assert_consistent(); assert_eq!(l.head(), 1); - l.assert_consistent(); assert_eq!(l.tail(), 3); - l.assert_consistent(); assert_eq!(l.pop().get(), 1); - l.assert_consistent(); assert_eq!(l.pop().get(), 2); - l.assert_consistent(); assert_eq!(l.pop().get(), 3); - l.assert_consistent(); assert!(l.is_empty()); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_asymmetric_link() { - let l = DList::<int>(); - let _one = l.push_n(1); - let two = l.push_n(2); - two.prev = None; - l.assert_consistent(); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_cyclic_list() { - let l = DList::<int>(); - let one = l.push_n(1); - let _two = l.push_n(2); - let three = l.push_n(3); - three.next = Some(one); - one.prev = Some(three); - l.assert_consistent(); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_headless() { - DList::<int>().head(); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_insert_already_present_before() { - let l = DList::<int>(); - let one = l.push_n(1); - let two = l.push_n(2); - l.insert_n_before(two, one); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_insert_already_present_after() { - let l = DList::<int>(); - let one = l.push_n(1); - let two = l.push_n(2); - l.insert_n_after(one, two); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_insert_before_orphan() { - let l = DList::<int>(); - let one = new_dlist_node(1); - let two = new_dlist_node(2); - l.insert_n_before(one, two); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_dlist_insert_after_orphan() { - let l = DList::<int>(); - let one = new_dlist_node(1); - let two = new_dlist_node(2); - l.insert_n_after(two, one); - } -} diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs deleted file mode 100644 index a7c18ebf5cd..00000000000 --- a/src/libstd/ebml.rs +++ /dev/null @@ -1,957 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Simple Extensible Binary Markup Language (ebml) reader and writer on a -// cursor model. See the specification here: -// http://www.matroska.org/technical/specs/rfc/index.html - -// Common data structures -struct EbmlTag { - id: uint, - size: uint, -} - -struct EbmlState { - ebml_tag: EbmlTag, - tag_pos: uint, - data_pos: uint, -} - -pub struct Doc { - data: @~[u8], - start: uint, - end: uint, -} - -pub struct TaggedDoc { - tag: uint, - doc: Doc, -} - -pub enum EbmlEncoderTag { - EsUint, // 0 - EsU64, // 1 - EsU32, // 2 - EsU16, // 3 - EsU8, // 4 - EsInt, // 5 - EsI64, // 6 - EsI32, // 7 - EsI16, // 8 - EsI8, // 9 - EsBool, // 10 - EsStr, // 11 - EsF64, // 12 - EsF32, // 13 - EsFloat, // 14 - EsEnum, // 15 - EsEnumVid, // 16 - EsEnumBody, // 17 - EsVec, // 18 - EsVecLen, // 19 - EsVecElt, // 20 - - EsOpaque, - - EsLabel // Used only when debugging -} -// -------------------------------------- - -pub mod reader { - use ebml::{Doc, EbmlEncoderTag, EsBool, EsEnum, EsEnumBody, EsEnumVid}; - use ebml::{EsI16, EsI32, EsI64, EsI8, EsInt}; - use ebml::{EsLabel, EsOpaque, EsStr, EsU16, EsU32, EsU64, EsU8, EsUint}; - use ebml::{EsVec, EsVecElt, EsVecLen, TaggedDoc}; - use serialize; - - use core::cast::transmute; - use core::int; - use core::io; - use core::ptr::offset; - use core::str; - use core::unstable::intrinsics::bswap32; - use core::vec; - - // ebml reading - - pub impl Doc { - fn get(&self, tag: uint) -> Doc { - get_doc(*self, tag) - } - } - - struct Res { - val: uint, - next: uint - } - - #[inline(never)] - fn vuint_at_slow(data: &[u8], start: uint) -> Res { - let a = data[start]; - if a & 0x80u8 != 0u8 { - return Res {val: (a & 0x7fu8) as uint, next: start + 1u}; - } - if a & 0x40u8 != 0u8 { - return Res {val: ((a & 0x3fu8) as uint) << 8u | - (data[start + 1u] as uint), - next: start + 2u}; - } - if a & 0x20u8 != 0u8 { - return Res {val: ((a & 0x1fu8) as uint) << 16u | - (data[start + 1u] as uint) << 8u | - (data[start + 2u] as uint), - next: start + 3u}; - } - if a & 0x10u8 != 0u8 { - return Res {val: ((a & 0x0fu8) as uint) << 24u | - (data[start + 1u] as uint) << 16u | - (data[start + 2u] as uint) << 8u | - (data[start + 3u] as uint), - next: start + 4u}; - } - fail!("vint too big"); - } - - #[cfg(target_arch = "x86")] - #[cfg(target_arch = "x86_64")] - pub fn vuint_at(data: &[u8], start: uint) -> Res { - if data.len() - start < 4 { - return vuint_at_slow(data, start); - } - - unsafe { - let (ptr, _): (*u8, uint) = transmute(data); - let ptr = offset(ptr, start); - let ptr: *i32 = transmute(ptr); - let val = bswap32(*ptr); - let val: u32 = transmute(val); - if (val & 0x80000000) != 0 { - Res { - val: ((val >> 24) & 0x7f) as uint, - next: start + 1 - } - } else if (val & 0x40000000) != 0 { - Res { - val: ((val >> 16) & 0x3fff) as uint, - next: start + 2 - } - } else if (val & 0x20000000) != 0 { - Res { - val: ((val >> 8) & 0x1fffff) as uint, - next: start + 3 - } - } else { - Res { - val: (val & 0x0fffffff) as uint, - next: start + 4 - } - } - } - } - - #[cfg(target_arch = "arm")] - #[cfg(target_arch = "mips")] - pub fn vuint_at(data: &[u8], start: uint) -> Res { - vuint_at_slow(data, start) - } - - pub fn Doc(data: @~[u8]) -> Doc { - Doc { data: data, start: 0u, end: vec::len::<u8>(*data) } - } - - pub fn doc_at(data: @~[u8], start: uint) -> TaggedDoc { - let elt_tag = vuint_at(*data, start); - let elt_size = vuint_at(*data, elt_tag.next); - let end = elt_size.next + elt_size.val; - TaggedDoc { - tag: elt_tag.val, - doc: Doc { data: data, start: elt_size.next, end: end } - } - } - - pub fn maybe_get_doc(d: Doc, tg: uint) -> Option<Doc> { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - if elt_tag.val == tg { - return Some(Doc { data: d.data, start: elt_size.next, - end: pos }); - } - } - None - } - - pub fn get_doc(d: Doc, tg: uint) -> Doc { - match maybe_get_doc(d, tg) { - Some(d) => d, - None => { - error!("failed to find block with tag %u", tg); - fail!(); - } - } - } - - pub fn docs(d: Doc, it: &fn(uint, Doc) -> bool) -> bool { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - let doc = Doc { data: d.data, start: elt_size.next, end: pos }; - if !it(elt_tag.val, doc) { - return false; - } - } - return true; - } - - pub fn tagged_docs(d: Doc, tg: uint, it: &fn(Doc) -> bool) -> bool { - let mut pos = d.start; - while pos < d.end { - let elt_tag = vuint_at(*d.data, pos); - let elt_size = vuint_at(*d.data, elt_tag.next); - pos = elt_size.next + elt_size.val; - if elt_tag.val == tg { - let doc = Doc { data: d.data, start: elt_size.next, - end: pos }; - if !it(doc) { - return false; - } - } - } - return true; - } - - pub fn doc_data(d: Doc) -> ~[u8] { - vec::slice::<u8>(*d.data, d.start, d.end).to_vec() - } - - pub fn with_doc_data<T>(d: Doc, f: &fn(x: &[u8]) -> T) -> T { - f(vec::slice(*d.data, d.start, d.end)) - } - - pub fn doc_as_str(d: Doc) -> ~str { str::from_bytes(doc_data(d)) } - - pub fn doc_as_u8(d: Doc) -> u8 { - assert_eq!(d.end, d.start + 1u); - (*d.data)[d.start] - } - - pub fn doc_as_u16(d: Doc) -> u16 { - assert_eq!(d.end, d.start + 2u); - io::u64_from_be_bytes(*d.data, d.start, 2u) as u16 - } - - pub fn doc_as_u32(d: Doc) -> u32 { - assert_eq!(d.end, d.start + 4u); - io::u64_from_be_bytes(*d.data, d.start, 4u) as u32 - } - - pub fn doc_as_u64(d: Doc) -> u64 { - assert_eq!(d.end, d.start + 8u); - io::u64_from_be_bytes(*d.data, d.start, 8u) - } - - pub fn doc_as_i8(d: Doc) -> i8 { doc_as_u8(d) as i8 } - pub fn doc_as_i16(d: Doc) -> i16 { doc_as_u16(d) as i16 } - pub fn doc_as_i32(d: Doc) -> i32 { doc_as_u32(d) as i32 } - pub fn doc_as_i64(d: Doc) -> i64 { doc_as_u64(d) as i64 } - - pub struct Decoder { - priv parent: Doc, - priv pos: uint, - } - - pub fn Decoder(d: Doc) -> Decoder { - Decoder { - parent: d, - pos: d.start - } - } - - priv impl Decoder { - fn _check_label(&mut self, lbl: &str) { - if self.pos < self.parent.end { - let TaggedDoc { tag: r_tag, doc: r_doc } = - doc_at(self.parent.data, self.pos); - - if r_tag == (EsLabel as uint) { - self.pos = r_doc.end; - let str = doc_as_str(r_doc); - if lbl != str { - fail!("Expected label %s but found %s", lbl, str); - } - } - } - } - - fn next_doc(&mut self, exp_tag: EbmlEncoderTag) -> Doc { - debug!(". next_doc(exp_tag=%?)", exp_tag); - if self.pos >= self.parent.end { - fail!("no more documents in current node!"); - } - let TaggedDoc { tag: r_tag, doc: r_doc } = - doc_at(self.parent.data, self.pos); - debug!("self.parent=%?-%? self.pos=%? r_tag=%? r_doc=%?-%?", - copy self.parent.start, copy self.parent.end, - copy self.pos, r_tag, r_doc.start, r_doc.end); - if r_tag != (exp_tag as uint) { - fail!("expected EBML doc with tag %? but found tag %?", exp_tag, r_tag); - } - if r_doc.end > self.parent.end { - fail!("invalid EBML, child extends to 0x%x, parent to 0x%x", - r_doc.end, self.parent.end); - } - self.pos = r_doc.end; - r_doc - } - - fn push_doc<T>(&mut self, d: Doc, f: &fn() -> T) -> T { - let old_parent = self.parent; - let old_pos = self.pos; - self.parent = d; - self.pos = d.start; - let r = f(); - self.parent = old_parent; - self.pos = old_pos; - r - } - - fn _next_uint(&mut self, exp_tag: EbmlEncoderTag) -> uint { - let r = doc_as_u32(self.next_doc(exp_tag)); - debug!("_next_uint exp_tag=%? result=%?", exp_tag, r); - r as uint - } - } - - pub impl Decoder { - fn read_opaque<R>(&mut self, op: &fn(&mut Decoder, Doc) -> R) -> R { - let doc = self.next_doc(EsOpaque); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = doc.start; - - let result = op(self, doc); - - self.parent = old_parent; - self.pos = old_pos; - result - } - } - - impl serialize::Decoder for Decoder { - fn read_nil(&mut self) -> () { () } - - fn read_u64(&mut self) -> u64 { doc_as_u64(self.next_doc(EsU64)) } - fn read_u32(&mut self) -> u32 { doc_as_u32(self.next_doc(EsU32)) } - fn read_u16(&mut self) -> u16 { doc_as_u16(self.next_doc(EsU16)) } - fn read_u8 (&mut self) -> u8 { doc_as_u8 (self.next_doc(EsU8 )) } - fn read_uint(&mut self) -> uint { - let v = doc_as_u64(self.next_doc(EsUint)); - if v > (::core::uint::max_value as u64) { - fail!("uint %? too large for this architecture", v); - } - v as uint - } - - fn read_i64(&mut self) -> i64 { - doc_as_u64(self.next_doc(EsI64)) as i64 - } - fn read_i32(&mut self) -> i32 { - doc_as_u32(self.next_doc(EsI32)) as i32 - } - fn read_i16(&mut self) -> i16 { - doc_as_u16(self.next_doc(EsI16)) as i16 - } - fn read_i8 (&mut self) -> i8 { - doc_as_u8(self.next_doc(EsI8 )) as i8 - } - fn read_int(&mut self) -> int { - let v = doc_as_u64(self.next_doc(EsInt)) as i64; - if v > (int::max_value as i64) || v < (int::min_value as i64) { - debug!("FIXME #6122: Removing this makes this function miscompile"); - fail!("int %? out of range for this architecture", v); - } - v as int - } - - fn read_bool(&mut self) -> bool { - doc_as_u8(self.next_doc(EsBool)) as bool - } - - fn read_f64(&mut self) -> f64 { fail!("read_f64()"); } - fn read_f32(&mut self) -> f32 { fail!("read_f32()"); } - fn read_float(&mut self) -> float { fail!("read_float()"); } - fn read_char(&mut self) -> char { fail!("read_char()"); } - fn read_str(&mut self) -> ~str { doc_as_str(self.next_doc(EsStr)) } - - // Compound types: - fn read_enum<T>(&mut self, - name: &str, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_enum(%s)", name); - self._check_label(name); - - let doc = self.next_doc(EsEnum); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = self.parent.start; - - let result = f(self); - - self.parent = old_parent; - self.pos = old_pos; - result - } - - fn read_enum_variant<T>(&mut self, - _: &[&str], - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_enum_variant()"); - let idx = self._next_uint(EsEnumVid); - debug!(" idx=%u", idx); - - let doc = self.next_doc(EsEnumBody); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = self.parent.start; - - let result = f(self, idx); - - self.parent = old_parent; - self.pos = old_pos; - result - } - - fn read_enum_variant_arg<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) -> T { - debug!("read_enum_variant_arg(idx=%u)", idx); - f(self) - } - - fn read_enum_struct_variant<T>(&mut self, - _: &[&str], - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_enum_struct_variant()"); - let idx = self._next_uint(EsEnumVid); - debug!(" idx=%u", idx); - - let doc = self.next_doc(EsEnumBody); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = self.parent.start; - - let result = f(self, idx); - - self.parent = old_parent; - self.pos = old_pos; - result - } - - fn read_enum_struct_variant_field<T>(&mut self, - name: &str, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_enum_struct_variant_arg(name=%?, idx=%u)", name, idx); - f(self) - } - - fn read_struct<T>(&mut self, - name: &str, - _: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_struct(name=%s)", name); - f(self) - } - - fn read_struct_field<T>(&mut self, - name: &str, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_struct_field(name=%?, idx=%u)", name, idx); - self._check_label(name); - f(self) - } - - fn read_tuple<T>(&mut self, f: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_tuple()"); - self.read_seq(f) - } - - fn read_tuple_arg<T>(&mut self, idx: uint, f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_tuple_arg(idx=%u)", idx); - self.read_seq_elt(idx, f) - } - - fn read_tuple_struct<T>(&mut self, - name: &str, - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_tuple_struct(name=%?)", name); - self.read_tuple(f) - } - - fn read_tuple_struct_arg<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_tuple_struct_arg(idx=%u)", idx); - self.read_tuple_arg(idx, f) - } - - fn read_option<T>(&mut self, f: &fn(&mut Decoder, bool) -> T) -> T { - debug!("read_option()"); - do self.read_enum("Option") |this| { - do this.read_enum_variant(["None", "Some"]) |this, idx| { - match idx { - 0 => f(this, false), - 1 => f(this, true), - _ => fail!(), - } - } - } - } - - fn read_seq<T>(&mut self, f: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_seq()"); - let doc = self.next_doc(EsVec); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = self.parent.start; - - let len = self._next_uint(EsVecLen); - debug!(" len=%u", len); - let result = f(self, len); - - self.parent = old_parent; - self.pos = old_pos; - result - } - - fn read_seq_elt<T>(&mut self, idx: uint, f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_seq_elt(idx=%u)", idx); - let doc = self.next_doc(EsVecElt); - - let (old_parent, old_pos) = (self.parent, self.pos); - self.parent = doc; - self.pos = self.parent.start; - - let result = f(self); - - self.parent = old_parent; - self.pos = old_pos; - result - } - - fn read_map<T>(&mut self, _: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_map()"); - fail!("read_map is unimplemented"); - } - - fn read_map_elt_key<T>(&mut self, - idx: uint, - _: &fn(&mut Decoder) -> T) - -> T { - debug!("read_map_elt_key(idx=%u)", idx); - fail!("read_map_elt_val is unimplemented"); - } - - fn read_map_elt_val<T>(&mut self, - idx: uint, - _: &fn(&mut Decoder) -> T) - -> T { - debug!("read_map_elt_val(idx=%u)", idx); - fail!("read_map_elt_val is unimplemented"); - } - } -} - -pub mod writer { - use ebml::{EbmlEncoderTag, EsBool, EsEnum, EsEnumBody, EsEnumVid}; - use ebml::{EsI16, EsI32, EsI64, EsI8, EsInt}; - use ebml::{EsLabel, EsOpaque, EsStr, EsU16, EsU32, EsU64, EsU8, EsUint}; - use ebml::{EsVec, EsVecElt, EsVecLen}; - - use core::io; - use core::str; - - // ebml writing - pub struct Encoder { - writer: @io::Writer, - priv size_positions: ~[uint], - } - - fn write_sized_vuint(w: @io::Writer, n: uint, size: uint) { - match size { - 1u => w.write(&[0x80u8 | (n as u8)]), - 2u => w.write(&[0x40u8 | ((n >> 8_u) as u8), n as u8]), - 3u => w.write(&[0x20u8 | ((n >> 16_u) as u8), (n >> 8_u) as u8, - n as u8]), - 4u => w.write(&[0x10u8 | ((n >> 24_u) as u8), (n >> 16_u) as u8, - (n >> 8_u) as u8, n as u8]), - _ => fail!("vint to write too big: %?", n) - }; - } - - fn write_vuint(w: @io::Writer, n: uint) { - if n < 0x7f_u { write_sized_vuint(w, n, 1u); return; } - if n < 0x4000_u { write_sized_vuint(w, n, 2u); return; } - if n < 0x200000_u { write_sized_vuint(w, n, 3u); return; } - if n < 0x10000000_u { write_sized_vuint(w, n, 4u); return; } - fail!("vint to write too big: %?", n); - } - - pub fn Encoder(w: @io::Writer) -> Encoder { - let size_positions: ~[uint] = ~[]; - Encoder { - writer: w, - size_positions: size_positions - } - } - - // FIXME (#2741): Provide a function to write the standard ebml header. - pub impl Encoder { - fn start_tag(&mut self, tag_id: uint) { - debug!("Start tag %u", tag_id); - - // Write the enum ID: - write_vuint(self.writer, tag_id); - - // Write a placeholder four-byte size. - self.size_positions.push(self.writer.tell()); - let zeroes: &[u8] = &[0u8, 0u8, 0u8, 0u8]; - self.writer.write(zeroes); - } - - fn end_tag(&mut self) { - let last_size_pos = self.size_positions.pop(); - let cur_pos = self.writer.tell(); - self.writer.seek(last_size_pos as int, io::SeekSet); - let size = (cur_pos - last_size_pos - 4u); - write_sized_vuint(self.writer, size, 4u); - self.writer.seek(cur_pos as int, io::SeekSet); - - debug!("End tag (size = %u)", size); - } - - fn wr_tag(&mut self, tag_id: uint, blk: &fn()) { - self.start_tag(tag_id); - blk(); - self.end_tag(); - } - - fn wr_tagged_bytes(&mut self, tag_id: uint, b: &[u8]) { - write_vuint(self.writer, tag_id); - write_vuint(self.writer, b.len()); - self.writer.write(b); - } - - fn wr_tagged_u64(&mut self, tag_id: uint, v: u64) { - do io::u64_to_be_bytes(v, 8u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_u32(&mut self, tag_id: uint, v: u32) { - do io::u64_to_be_bytes(v as u64, 4u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_u16(&mut self, tag_id: uint, v: u16) { - do io::u64_to_be_bytes(v as u64, 2u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_u8(&mut self, tag_id: uint, v: u8) { - self.wr_tagged_bytes(tag_id, &[v]); - } - - fn wr_tagged_i64(&mut self, tag_id: uint, v: i64) { - do io::u64_to_be_bytes(v as u64, 8u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_i32(&mut self, tag_id: uint, v: i32) { - do io::u64_to_be_bytes(v as u64, 4u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_i16(&mut self, tag_id: uint, v: i16) { - do io::u64_to_be_bytes(v as u64, 2u) |v| { - self.wr_tagged_bytes(tag_id, v); - } - } - - fn wr_tagged_i8(&mut self, tag_id: uint, v: i8) { - self.wr_tagged_bytes(tag_id, &[v as u8]); - } - - fn wr_tagged_str(&mut self, tag_id: uint, v: &str) { - str::byte_slice(v, |b| self.wr_tagged_bytes(tag_id, b)); - } - - fn wr_bytes(&mut self, b: &[u8]) { - debug!("Write %u bytes", b.len()); - self.writer.write(b); - } - - fn wr_str(&mut self, s: &str) { - debug!("Write str: %?", s); - self.writer.write(str::to_bytes(s)); - } - } - - // FIXME (#2743): optionally perform "relaxations" on end_tag to more - // efficiently encode sizes; this is a fixed point iteration - - // Set to true to generate more debugging in EBML code. - // Totally lame approach. - static debug: bool = true; - - priv impl Encoder { - // used internally to emit things like the vector length and so on - fn _emit_tagged_uint(&mut self, t: EbmlEncoderTag, v: uint) { - assert!(v <= 0xFFFF_FFFF_u); - self.wr_tagged_u32(t as uint, v as u32); - } - - fn _emit_label(&mut self, label: &str) { - // There are various strings that we have access to, such as - // the name of a record field, which do not actually appear in - // the encoded EBML (normally). This is just for - // efficiency. When debugging, though, we can emit such - // labels and then they will be checked by decoder to - // try and check failures more quickly. - if debug { self.wr_tagged_str(EsLabel as uint, label) } - } - } - - pub impl Encoder { - fn emit_opaque(&mut self, f: &fn(&mut Encoder)) { - self.start_tag(EsOpaque as uint); - f(self); - self.end_tag(); - } - } - - impl ::serialize::Encoder for Encoder { - fn emit_nil(&mut self) {} - - fn emit_uint(&mut self, v: uint) { - self.wr_tagged_u64(EsUint as uint, v as u64); - } - fn emit_u64(&mut self, v: u64) { - self.wr_tagged_u64(EsU64 as uint, v); - } - fn emit_u32(&mut self, v: u32) { - self.wr_tagged_u32(EsU32 as uint, v); - } - fn emit_u16(&mut self, v: u16) { - self.wr_tagged_u16(EsU16 as uint, v); - } - fn emit_u8(&mut self, v: u8) { - self.wr_tagged_u8(EsU8 as uint, v); - } - - fn emit_int(&mut self, v: int) { - self.wr_tagged_i64(EsInt as uint, v as i64); - } - fn emit_i64(&mut self, v: i64) { - self.wr_tagged_i64(EsI64 as uint, v); - } - fn emit_i32(&mut self, v: i32) { - self.wr_tagged_i32(EsI32 as uint, v); - } - fn emit_i16(&mut self, v: i16) { - self.wr_tagged_i16(EsI16 as uint, v); - } - fn emit_i8(&mut self, v: i8) { - self.wr_tagged_i8(EsI8 as uint, v); - } - - fn emit_bool(&mut self, v: bool) { - self.wr_tagged_u8(EsBool as uint, v as u8) - } - - // FIXME (#2742): implement these - fn emit_f64(&mut self, _v: f64) { - fail!("Unimplemented: serializing an f64"); - } - fn emit_f32(&mut self, _v: f32) { - fail!("Unimplemented: serializing an f32"); - } - fn emit_float(&mut self, _v: float) { - fail!("Unimplemented: serializing a float"); - } - - fn emit_char(&mut self, _v: char) { - fail!("Unimplemented: serializing a char"); - } - - fn emit_str(&mut self, v: &str) { - self.wr_tagged_str(EsStr as uint, v) - } - - fn emit_enum(&mut self, name: &str, f: &fn(&mut Encoder)) { - self._emit_label(name); - self.start_tag(EsEnum as uint); - f(self); - self.end_tag(); - } - - fn emit_enum_variant(&mut self, - _: &str, - v_id: uint, - _: uint, - f: &fn(&mut Encoder)) { - self._emit_tagged_uint(EsEnumVid, v_id); - self.start_tag(EsEnumBody as uint); - f(self); - self.end_tag(); - } - - fn emit_enum_variant_arg(&mut self, _: uint, f: &fn(&mut Encoder)) { - f(self) - } - - fn emit_enum_struct_variant(&mut self, - v_name: &str, - v_id: uint, - cnt: uint, - f: &fn(&mut Encoder)) { - self.emit_enum_variant(v_name, v_id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, - _: &str, - idx: uint, - f: &fn(&mut Encoder)) { - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, _len: uint, f: &fn(&mut Encoder)) { - f(self) - } - - fn emit_struct_field(&mut self, - name: &str, - _: uint, - f: &fn(&mut Encoder)) { - self._emit_label(name); - f(self) - } - - fn emit_tuple(&mut self, len: uint, f: &fn(&mut Encoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: uint, f: &fn(&mut Encoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, - _: &str, - len: uint, - f: &fn(&mut Encoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: uint, f: &fn(&mut Encoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: &fn(&mut Encoder)) { - self.emit_enum("Option", f); - } - fn emit_option_none(&mut self) { - self.emit_enum_variant("None", 0, 0, |_| ()) - } - fn emit_option_some(&mut self, f: &fn(&mut Encoder)) { - self.emit_enum_variant("Some", 1, 1, f) - } - - fn emit_seq(&mut self, len: uint, f: &fn(&mut Encoder)) { - self.start_tag(EsVec as uint); - self._emit_tagged_uint(EsVecLen, len); - f(self); - self.end_tag(); - } - - fn emit_seq_elt(&mut self, _idx: uint, f: &fn(&mut Encoder)) { - self.start_tag(EsVecElt as uint); - f(self); - self.end_tag(); - } - - fn emit_map(&mut self, _len: uint, _f: &fn(&mut Encoder)) { - fail!("emit_map is unimplemented"); - } - - fn emit_map_elt_key(&mut self, _idx: uint, _f: &fn(&mut Encoder)) { - fail!("emit_map_elt_key is unimplemented"); - } - - fn emit_map_elt_val(&mut self, _idx: uint, _f: &fn(&mut Encoder)) { - fail!("emit_map_elt_val is unimplemented"); - } - } -} - -// ___________________________________________________________________________ -// Testing - -#[cfg(test)] -mod tests { - use ebml::reader; - use ebml::writer; - use serialize::Encodable; - use serialize; - - use core::io; - use core::option::{None, Option, Some}; - - #[test] - fn test_option_int() { - fn test_v(v: Option<int>) { - debug!("v == %?", v); - let bytes = do io::with_bytes_writer |wr| { - let mut ebml_w = writer::Encoder(wr); - v.encode(&mut ebml_w) - }; - let ebml_doc = reader::Doc(@bytes); - let mut deser = reader::Decoder(ebml_doc); - let v1 = serialize::Decodable::decode(&mut deser); - debug!("v1 == %?", v1); - assert_eq!(v, v1); - } - - test_v(Some(22)); - test_v(None); - test_v(Some(3)); - } -} diff --git a/src/libstd/either.rs b/src/libstd/either.rs new file mode 100644 index 00000000000..f89bb3b2f90 --- /dev/null +++ b/src/libstd/either.rs @@ -0,0 +1,256 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! A type that represents one of two alternatives + +use container::Container; +use cmp::Eq; +use kinds::Copy; +use old_iter::BaseIter; +use result::Result; +use result; +use vec; +use vec::OwnedVector; + +/// The either type +#[deriving(Clone, Eq)] +pub enum Either<T, U> { + Left(T), + Right(U) +} + +/// Applies a function based on the given either value +/// +/// If `value` is left(T) then `f_left` is applied to its contents, if +/// `value` is right(U) then `f_right` is applied to its contents, and the +/// result is returned. +#[inline(always)] +pub fn either<T, U, V>(f_left: &fn(&T) -> V, + f_right: &fn(&U) -> V, value: &Either<T, U>) -> V { + match *value { + Left(ref l) => f_left(l), + Right(ref r) => f_right(r) + } +} + +/// Extracts from a vector of either all the left values +pub fn lefts<T:Copy,U>(eithers: &[Either<T, U>]) -> ~[T] { + do vec::build_sized(eithers.len()) |push| { + for eithers.each |elt| { + match *elt { + Left(ref l) => { push(*l); } + _ => { /* fallthrough */ } + } + } + } +} + +/// Extracts from a vector of either all the right values +pub fn rights<T, U: Copy>(eithers: &[Either<T, U>]) -> ~[U] { + do vec::build_sized(eithers.len()) |push| { + for eithers.each |elt| { + match *elt { + Right(ref r) => { push(*r); } + _ => { /* fallthrough */ } + } + } + } +} + +/// Extracts from a vector of either all the left values and right values +/// +/// Returns a structure containing a vector of left values and a vector of +/// right values. +pub fn partition<T, U>(eithers: ~[Either<T, U>]) -> (~[T], ~[U]) { + let mut lefts: ~[T] = ~[]; + let mut rights: ~[U] = ~[]; + do vec::consume(eithers) |_i, elt| { + match elt { + Left(l) => lefts.push(l), + Right(r) => rights.push(r) + } + } + return (lefts, rights); +} + +/// Flips between left and right of a given either +#[inline(always)] +pub fn flip<T, U>(eith: Either<T, U>) -> Either<U, T> { + match eith { + Right(r) => Left(r), + Left(l) => Right(l) + } +} + +/// Converts either::t to a result::t +/// +/// Converts an `either` type to a `result` type, making the "right" choice +/// an ok result, and the "left" choice a fail +#[inline(always)] +pub fn to_result<T, U>(eith: Either<T, U>) -> Result<U, T> { + match eith { + Right(r) => result::Ok(r), + Left(l) => result::Err(l) + } +} + +/// Checks whether the given value is a left +#[inline(always)] +pub fn is_left<T, U>(eith: &Either<T, U>) -> bool { + match *eith { + Left(_) => true, + _ => false + } +} + +/// Checks whether the given value is a right +#[inline(always)] +pub fn is_right<T, U>(eith: &Either<T, U>) -> bool { + match *eith { + Right(_) => true, + _ => false + } +} + +/// Retrieves the value in the left branch. Fails if the either is Right. +#[inline(always)] +pub fn unwrap_left<T,U>(eith: Either<T,U>) -> T { + match eith { + Left(x) => x, + Right(_) => fail!("either::unwrap_left Right") + } +} + +/// Retrieves the value in the right branch. Fails if the either is Left. +#[inline(always)] +pub fn unwrap_right<T,U>(eith: Either<T,U>) -> U { + match eith { + Right(x) => x, + Left(_) => fail!("either::unwrap_right Left") + } +} + +pub impl<T, U> Either<T, U> { + #[inline(always)] + fn either<V>(&self, f_left: &fn(&T) -> V, f_right: &fn(&U) -> V) -> V { + either(f_left, f_right, self) + } + + #[inline(always)] + fn flip(self) -> Either<U, T> { flip(self) } + + #[inline(always)] + fn to_result(self) -> Result<U, T> { to_result(self) } + + #[inline(always)] + fn is_left(&self) -> bool { is_left(self) } + + #[inline(always)] + fn is_right(&self) -> bool { is_right(self) } + + #[inline(always)] + fn unwrap_left(self) -> T { unwrap_left(self) } + + #[inline(always)] + fn unwrap_right(self) -> U { unwrap_right(self) } +} + +#[test] +fn test_either_left() { + let val = Left(10); + fn f_left(x: &int) -> bool { *x == 10 } + fn f_right(_x: &uint) -> bool { false } + assert!((either(f_left, f_right, &val))); +} + +#[test] +fn test_either_right() { + let val = Right(10u); + fn f_left(_x: &int) -> bool { false } + fn f_right(x: &uint) -> bool { *x == 10u } + assert!((either(f_left, f_right, &val))); +} + +#[test] +fn test_lefts() { + let input = ~[Left(10), Right(11), Left(12), Right(13), Left(14)]; + let result = lefts(input); + assert_eq!(result, ~[10, 12, 14]); +} + +#[test] +fn test_lefts_none() { + let input: ~[Either<int, int>] = ~[Right(10), Right(10)]; + let result = lefts(input); + assert_eq!(result.len(), 0u); +} + +#[test] +fn test_lefts_empty() { + let input: ~[Either<int, int>] = ~[]; + let result = lefts(input); + assert_eq!(result.len(), 0u); +} + +#[test] +fn test_rights() { + let input = ~[Left(10), Right(11), Left(12), Right(13), Left(14)]; + let result = rights(input); + assert_eq!(result, ~[11, 13]); +} + +#[test] +fn test_rights_none() { + let input: ~[Either<int, int>] = ~[Left(10), Left(10)]; + let result = rights(input); + assert_eq!(result.len(), 0u); +} + +#[test] +fn test_rights_empty() { + let input: ~[Either<int, int>] = ~[]; + let result = rights(input); + assert_eq!(result.len(), 0u); +} + +#[test] +fn test_partition() { + let input = ~[Left(10), Right(11), Left(12), Right(13), Left(14)]; + let (lefts, rights) = partition(input); + assert_eq!(lefts[0], 10); + assert_eq!(lefts[1], 12); + assert_eq!(lefts[2], 14); + assert_eq!(rights[0], 11); + assert_eq!(rights[1], 13); +} + +#[test] +fn test_partition_no_lefts() { + let input: ~[Either<int, int>] = ~[Right(10), Right(11)]; + let (lefts, rights) = partition(input); + assert_eq!(lefts.len(), 0u); + assert_eq!(rights.len(), 2u); +} + +#[test] +fn test_partition_no_rights() { + let input: ~[Either<int, int>] = ~[Left(10), Left(11)]; + let (lefts, rights) = partition(input); + assert_eq!(lefts.len(), 2u); + assert_eq!(rights.len(), 0u); +} + +#[test] +fn test_partition_empty() { + let input: ~[Either<int, int>] = ~[]; + let (lefts, rights) = partition(input); + assert_eq!(lefts.len(), 0u); + assert_eq!(rights.len(), 0u); +} diff --git a/src/libstd/fileinput.rs b/src/libstd/fileinput.rs deleted file mode 100644 index d9a932fbe7c..00000000000 --- a/src/libstd/fileinput.rs +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! -A library for iterating through the lines in a series of -files. Very similar to [the Python module of the same -name](http://docs.python.org/3.3/library/fileinput.html). - -It allows the programmer to automatically take filenames from the -command line arguments (via `input` and `input_state`), as well as -specify them as a vector directly (`input_vec` and -`input_vec_state`). The files are opened as necessary, so any files -that can't be opened only cause an error when reached in the -iteration. - -On the command line, `stdin` is represented by a filename of `-` (a -single hyphen) and in the functions that take a vector directly -(e.g. `input_vec`) it is represented by `None`. Note `stdin` is *not* -reset once it has been finished, so attempting to iterate on `[None, -None]` will only take input once unless `io::stdin().seek(0, SeekSet)` -is called between. - -The `pathify` function handles converting a list of file paths as -strings to the appropriate format, including the (optional) conversion -of `"-"` to `stdin`. - -# Basic - -In many cases, one can use the `input_*` functions without having -to handle any `FileInput` structs. E.g. a simple `cat` program - - for input |line| { - io::println(line) - } - -or a program that numbers lines after concatenating two files - - for input_vec_state(pathify([~"a.txt", ~"b.txt"])) |line, state| { - io::println(fmt!("%u: %s", state.line_num, - line)); - } - -The two `input_vec*` functions take a vec of file names (where empty -means read from `stdin`), the other two functions use the command line -arguments. - -# Advanced - -For more complicated uses (e.g. if one needs to pause iteration and -resume it later), a `FileInput` instance can be constructed via the -`from_vec`, `from_vec_raw` and `from_args` functions. - -Once created, the `each_line` (from the `core::io::ReaderUtil` trait) -and `each_line_state` methods allow one to iterate on the lines; the -latter provides more information about the position within the -iteration to the caller. - -It is possible (and safe) to skip lines and files using the -`read_line` and `next_file` methods. Also, `FileInput` implements -`core::io::Reader`, and the state will be updated correctly while -using any of those methods. - -E.g. the following program reads until an empty line, pauses for user -input, skips the current file and then numbers the remaining lines -(where the numbers are from the start of each file, rather than the -total line count). - - let in = FileInput::from_vec(pathify([~"a.txt", ~"b.txt", ~"c.txt"], - true)); - - for in.each_line |line| { - if line.is_empty() { - break - } - io::println(line); - } - - io::println("Continue?"); - - if io::stdin().read_line() == ~"yes" { - in.next_file(); // skip! - - for in.each_line_state |line, state| { - io::println(fmt!("%u: %s", state.line_num_file, - line)) - } - } -*/ - -use core::io::ReaderUtil; - -/** -A summary of the internal state of a `FileInput` object. `line_num` -and `line_num_file` represent the number of lines read in total and in -the current file respectively. `current_path` is `None` if the current -file is `stdin`. -*/ -pub struct FileInputState { - current_path: Option<Path>, - line_num: uint, - line_num_file: uint -} - -impl FileInputState { - fn is_stdin(&self) -> bool { - self.current_path.is_none() - } - - fn is_first_line(&self) -> bool { - self.line_num_file == 1 - } -} - -struct FileInput_ { - /** - `Some(path)` is the file represented by `path`, `None` is - `stdin`. Consumed as the files are read. - */ - priv files: ~[Option<Path>], - /** - The current file: `Some(r)` for an open file, `None` before - starting and after reading everything. - */ - priv current_reader: Option<@io::Reader>, - priv state: FileInputState, - - /** - Used to keep track of whether we need to insert the newline at the - end of a file that is missing it, which is needed to separate the - last and first lines. - */ - priv previous_was_newline: bool -} - -// XXX: remove this when Reader has &mut self. Should be removable via -// "self.fi." -> "self." and renaming FileInput_. Documentation above -// will likely have to be updated to use `let mut in = ...`. -pub struct FileInput { - priv fi: @mut FileInput_ -} - -impl FileInput { - /** - Create a `FileInput` object from a vec of files. An empty - vec means lines are read from `stdin` (use `from_vec_raw` to stop - this behaviour). Any occurence of `None` represents `stdin`. - */ - pub fn from_vec(files: ~[Option<Path>]) -> FileInput { - FileInput::from_vec_raw( - if files.is_empty() { - ~[None] - } else { - files - }) - } - - /** - Identical to `from_vec`, but an empty `files` vec stays - empty. (`None` is `stdin`.) - */ - pub fn from_vec_raw(files: ~[Option<Path>]) - -> FileInput { - FileInput{ - fi: @mut FileInput_ { - files: files, - current_reader: None, - state: FileInputState { - current_path: None, - line_num: 0, - line_num_file: 0 - }, - // there was no previous unended line - previous_was_newline: true - } - } - } - - /** - Create a `FileInput` object from the command line - arguments. `"-"` represents `stdin`. - */ - pub fn from_args() -> FileInput { - let args = os::args(), - pathed = pathify(args.tail(), true); - FileInput::from_vec(pathed) - } - - priv fn current_file_eof(&self) -> bool { - match self.fi.current_reader { - None => false, - Some(r) => r.eof() - } - } - - /** - Skip to the next file in the queue. Can `fail` when opening - a file. - - Returns `false` if there is no more files, and `true` when it - successfully opens the next file. - */ - - pub fn next_file(&self) -> bool { - // No more files - - if self.fi.files.is_empty() { - self.fi.current_reader = None; - return false; - } - - let path_option = self.fi.files.shift(), - file = match path_option { - None => io::stdin(), - Some(ref path) => io::file_reader(path).get() - }; - - self.fi.current_reader = Some(file); - self.fi.state.current_path = path_option; - self.fi.state.line_num_file = 0; - true - } - - /** - Attempt to open the next file if there is none currently open, - or if the current one is EOF'd. - - Returns `true` if it had to move to the next file and did - so successfully. - */ - priv fn next_file_if_eof(&self) -> bool { - match self.fi.current_reader { - None => self.next_file(), - Some(r) => { - if r.eof() { - self.next_file() - } else { - false - } - } - } - } - - /** - Apply `f` to each line successively, along with some state - (line numbers and file names, see documentation for - `FileInputState`). Otherwise identical to `lines_each`. - */ - pub fn each_line_state(&self, - f: &fn(&str, FileInputState) -> bool) -> bool { - self.each_line(|line| f(line, copy self.fi.state)) - } - - - /** - Retrieve the current `FileInputState` information. - */ - pub fn state(&self) -> FileInputState { - copy self.fi.state - } -} - -impl io::Reader for FileInput { - fn read_byte(&self) -> int { - loop { - let stepped = self.next_file_if_eof(); - - // if we moved to the next file, and the previous - // character wasn't \n, then there is an unfinished line - // from the previous file. This library models - // line-by-line processing and the trailing line of the - // previous file and the leading of the current file - // should be considered different, so we need to insert a - // fake line separator - if stepped && !self.fi.previous_was_newline { - self.fi.state.line_num += 1; - self.fi.state.line_num_file += 1; - self.fi.previous_was_newline = true; - return '\n' as int; - } - - match self.fi.current_reader { - None => return -1, - Some(r) => { - let b = r.read_byte(); - - if b < 0 { - loop; - } - - if b == '\n' as int { - self.fi.state.line_num += 1; - self.fi.state.line_num_file += 1; - self.fi.previous_was_newline = true; - } else { - self.fi.previous_was_newline = false; - } - - return b; - } - } - } - } - fn read(&self, buf: &mut [u8], len: uint) -> uint { - let mut count = 0; - while count < len { - let b = self.read_byte(); - if b < 0 { break } - - buf[count] = b as u8; - count += 1; - } - - count - } - fn eof(&self) -> bool { - // we've run out of files, and current_reader is either None or eof. - - self.fi.files.is_empty() && - match self.fi.current_reader { None => true, Some(r) => r.eof() } - - } - fn seek(&self, offset: int, whence: io::SeekStyle) { - match self.fi.current_reader { - None => {}, - Some(r) => r.seek(offset, whence) - } - } - fn tell(&self) -> uint { - match self.fi.current_reader { - None => 0, - Some(r) => r.tell() - } - } -} - -/** -Convert a list of strings to an appropriate form for a `FileInput` -instance. `stdin_hyphen` controls whether `-` represents `stdin` or -a literal `-`. -*/ -// XXX: stupid, unclear name -pub fn pathify(vec: &[~str], stdin_hyphen : bool) -> ~[Option<Path>] { - vec::map(vec, |&str : & ~str| { - if stdin_hyphen && str == ~"-" { - None - } else { - Some(Path(str)) - } - }) -} - -/** -Iterate directly over the command line arguments (no arguments implies -reading from `stdin`). - -Fails when attempting to read from a file that can't be opened. -*/ -pub fn input(f: &fn(&str) -> bool) -> bool { - let i = FileInput::from_args(); - i.each_line(f) -} - -/** -Iterate directly over the command line arguments (no arguments -implies reading from `stdin`) with the current state of the iteration -provided at each call. - -Fails when attempting to read from a file that can't be opened. -*/ -pub fn input_state(f: &fn(&str, FileInputState) -> bool) -> bool { - let i = FileInput::from_args(); - i.each_line_state(f) -} - -/** -Iterate over a vector of files (an empty vector implies just `stdin`). - -Fails when attempting to read from a file that can't be opened. -*/ -pub fn input_vec(files: ~[Option<Path>], f: &fn(&str) -> bool) -> bool { - let i = FileInput::from_vec(files); - i.each_line(f) -} - -/** -Iterate over a vector of files (an empty vector implies just `stdin`) -with the current state of the iteration provided at each call. - -Fails when attempting to read from a file that can't be opened. -*/ -pub fn input_vec_state(files: ~[Option<Path>], - f: &fn(&str, FileInputState) -> bool) -> bool { - let i = FileInput::from_vec(files); - i.each_line_state(f) -} - -#[cfg(test)] -mod test { - use core::io::WriterUtil; - use super::{FileInput, pathify, input_vec, input_vec_state}; - - fn make_file(path : &Path, contents: &[~str]) { - let file = io::file_writer(path, [io::Create, io::Truncate]).get(); - - for contents.each |&str| { - file.write_str(str); - file.write_char('\n'); - } - } - - #[test] - fn test_pathify() { - let strs = [~"some/path", - ~"some/other/path"], - paths = ~[Some(Path("some/path")), - Some(Path("some/other/path"))]; - - assert_eq!(pathify(strs, true), copy paths); - assert_eq!(pathify(strs, false), paths); - - assert_eq!(pathify([~"-"], true), ~[None]); - assert_eq!(pathify([~"-"], false), ~[Some(Path("-"))]); - } - - #[test] - fn test_fileinput_read_byte() { - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-fileinput-read-byte-%u.tmp", i)), true); - - // 3 files containing 0\n, 1\n, and 2\n respectively - for filenames.eachi |i, &filename| { - make_file(filename.get_ref(), ~[fmt!("%u", i)]); - } - - let fi = FileInput::from_vec(copy filenames); - - for "012".each_chari |line, c| { - assert_eq!(fi.read_byte(), c as int); - assert_eq!(fi.state().line_num, line); - assert_eq!(fi.state().line_num_file, 0); - assert_eq!(fi.read_byte(), '\n' as int); - assert_eq!(fi.state().line_num, line + 1); - assert_eq!(fi.state().line_num_file, 1); - - assert_eq!(copy fi.state().current_path, copy filenames[line]); - } - - assert_eq!(fi.read_byte(), -1); - assert!(fi.eof()); - assert_eq!(fi.state().line_num, 3) - - } - - #[test] - fn test_fileinput_read() { - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-fileinput-read-%u.tmp", i)), true); - - // 3 files containing 1\n, 2\n, and 3\n respectively - for filenames.eachi |i, &filename| { - make_file(filename.get_ref(), ~[fmt!("%u", i)]); - } - - let fi = FileInput::from_vec(filenames); - let mut buf : ~[u8] = vec::from_elem(6, 0u8); - let count = fi.read(buf, 10); - assert_eq!(count, 6); - assert_eq!(buf, "0\n1\n2\n".to_bytes()); - assert!(fi.eof()) - assert_eq!(fi.state().line_num, 3); - } - - #[test] - fn test_input_vec() { - let mut all_lines = ~[]; - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-input-vec-%u.tmp", i)), true); - - for filenames.eachi |i, &filename| { - let contents = - vec::from_fn(3, |j| fmt!("%u %u", i, j)); - make_file(filename.get_ref(), contents); - all_lines.push_all(contents); - } - - let mut read_lines = ~[]; - for input_vec(filenames) |line| { - read_lines.push(line.to_owned()); - } - assert_eq!(read_lines, all_lines); - } - - #[test] - fn test_input_vec_state() { - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-input-vec-state-%u.tmp", i)),true); - - for filenames.eachi |i, &filename| { - let contents = - vec::from_fn(3, |j| fmt!("%u %u", i, j + 1)); - make_file(filename.get_ref(), contents); - } - - for input_vec_state(filenames) |line, state| { - let nums = do vec::build |p| { - for str::each_split_char(line, ' ') |s| { p(s.to_owned()); } - }; - let file_num = uint::from_str(nums[0]).get(); - let line_num = uint::from_str(nums[1]).get(); - assert_eq!(line_num, state.line_num_file); - assert_eq!(file_num * 3 + line_num, state.line_num); - } - } - - #[test] - fn test_empty_files() { - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-empty-files-%u.tmp", i)),true); - - make_file(filenames[0].get_ref(), ~[~"1", ~"2"]); - make_file(filenames[1].get_ref(), ~[]); - make_file(filenames[2].get_ref(), ~[~"3", ~"4"]); - - let mut count = 0; - for input_vec_state(copy filenames) |line, state| { - let expected_path = match line { - "1" | "2" => copy filenames[0], - "3" | "4" => copy filenames[2], - _ => fail!("unexpected line") - }; - assert_eq!(copy state.current_path, expected_path); - count += 1; - } - assert_eq!(count, 4); - } - - #[test] - fn test_no_trailing_newline() { - let f1 = Some(Path("tmp/lib-fileinput-test-no-trailing-newline-1.tmp")), - f2 = Some(Path("tmp/lib-fileinput-test-no-trailing-newline-2.tmp")); - - let wr = io::file_writer(f1.get_ref(), [io::Create, io::Truncate]).get(); - wr.write_str("1\n2"); - let wr = io::file_writer(f2.get_ref(), [io::Create, io::Truncate]).get(); - wr.write_str("3\n4"); - - let mut lines = ~[]; - for input_vec(~[f1, f2]) |line| { - lines.push(line.to_owned()); - } - assert_eq!(lines, ~[~"1", ~"2", ~"3", ~"4"]); - } - - - #[test] - fn test_next_file() { - let filenames = pathify(vec::from_fn( - 3, - |i| fmt!("tmp/lib-fileinput-test-next-file-%u.tmp", i)),true); - - for filenames.eachi |i, &filename| { - let contents = - vec::from_fn(3, |j| fmt!("%u %u", i, j + 1)); - make_file(&filename.get(), contents); - } - - let mut in = FileInput::from_vec(filenames); - - // read once from 0 - assert_eq!(in.read_line(), ~"0 1"); - in.next_file(); // skip the rest of 1 - - // read all lines from 1 (but don't read any from 2), - for uint::range(1, 4) |i| { - assert_eq!(in.read_line(), fmt!("1 %u", i)); - } - // 1 is finished, but 2 hasn't been started yet, so this will - // just "skip" to the beginning of 2 (Python's fileinput does - // the same) - in.next_file(); - - assert_eq!(in.read_line(), ~"2 1"); - } - - #[test] - #[should_fail] - fn test_input_vec_missing_file() { - for input_vec(pathify([~"this/file/doesnt/exist"], true)) |line| { - io::println(line); - } - } -} diff --git a/src/libstd/flate.rs b/src/libstd/flate.rs deleted file mode 100644 index b0d219bc945..00000000000 --- a/src/libstd/flate.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - -Simple compression - -*/ - -use libc; -use core::libc::{c_void, size_t, c_int}; -use vec; - -#[cfg(test)] use core::rand; -#[cfg(test)] use core::rand::RngUtil; - -pub mod rustrt { - use core::libc::{c_int, c_void, size_t}; - - #[link_name = "rustrt"] - pub extern { - unsafe fn tdefl_compress_mem_to_heap(psrc_buf: *const c_void, - src_buf_len: size_t, - pout_len: *mut size_t, - flags: c_int) - -> *c_void; - - unsafe fn tinfl_decompress_mem_to_heap(psrc_buf: *const c_void, - src_buf_len: size_t, - pout_len: *mut size_t, - flags: c_int) - -> *c_void; - } -} - -static lz_none : c_int = 0x0; // Huffman-coding only. -static lz_fast : c_int = 0x1; // LZ with only one probe -static lz_norm : c_int = 0x80; // LZ with 128 probes, "normal" -static lz_best : c_int = 0xfff; // LZ with 4095 probes, "best" - -pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] { - do vec::as_const_buf(bytes) |b, len| { - unsafe { - let mut outsz : size_t = 0; - let res = - rustrt::tdefl_compress_mem_to_heap(b as *c_void, - len as size_t, - &mut outsz, - lz_norm); - assert!(res as int != 0); - let out = vec::raw::from_buf_raw(res as *u8, - outsz as uint); - libc::free(res); - out - } - } -} - -pub fn inflate_bytes(bytes: &const [u8]) -> ~[u8] { - do vec::as_const_buf(bytes) |b, len| { - unsafe { - let mut outsz : size_t = 0; - let res = - rustrt::tinfl_decompress_mem_to_heap(b as *c_void, - len as size_t, - &mut outsz, - 0); - assert!(res as int != 0); - let out = vec::raw::from_buf_raw(res as *u8, - outsz as uint); - libc::free(res); - out - } - } -} - -#[test] -#[allow(non_implicitly_copyable_typarams)] -fn test_flate_round_trip() { - let mut r = rand::rng(); - let mut words = ~[]; - for 20.times { - let range = r.gen_uint_range(1, 10); - words.push(r.gen_bytes(range)); - } - for 20.times { - let mut in = ~[]; - for 2000.times { - in.push_all(r.choose(words)); - } - debug!("de/inflate of %u bytes of random word-sequences", - in.len()); - let cmp = deflate_bytes(in); - let out = inflate_bytes(cmp); - debug!("%u bytes deflated to %u (%.1f%% size)", - in.len(), cmp.len(), - 100.0 * ((cmp.len() as float) / (in.len() as float))); - assert_eq!(in, out); - } -} diff --git a/src/libstd/flatpipes.rs b/src/libstd/flatpipes.rs deleted file mode 100644 index a634f88dcf5..00000000000 --- a/src/libstd/flatpipes.rs +++ /dev/null @@ -1,962 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - -Generic communication channels for things that can be represented as, -or transformed to and from, byte vectors. - -The `FlatPort` and `FlatChan` types implement the generic channel and -port interface for arbitrary types and transport strategies. It can -particularly be used to send and receive serializable types over I/O -streams. - -`FlatPort` and `FlatChan` implement the same comm traits as pipe-based -ports and channels. - -# Example - -This example sends boxed integers across tasks using serialization. - -~~~ -let (port, chan) = serial::pipe_stream(); - -do task::spawn || { - for int::range(0, 10) |i| { - chan.send(@i) - } -} - -for int::range(0, 10) |i| { - assert @i == port.recv() -} -~~~ - -# Safety Note - -Flat pipes created from `io::Reader`s and `io::Writer`s share the same -blocking properties as the underlying stream. Since some implementations -block the scheduler thread, so will their pipes. - -*/ - -// The basic send/recv interface FlatChan and PortChan will implement -use core::io; -use core::comm::GenericChan; -use core::comm::GenericPort; -use core::sys::size_of; -use core::vec; - -/** -A FlatPort, consisting of a `BytePort` that receives byte vectors, -and an `Unflattener` that converts the bytes to a value. - -Create using the constructors in the `serial` and `pod` modules. -*/ -pub struct FlatPort<T, U, P> { - unflattener: U, - byte_port: P -} - -/** -A FlatChan, consisting of a `Flattener` that converts values to -byte vectors, and a `ByteChan` that transmits the bytes. - -Create using the constructors in the `serial` and `pod` modules. -*/ -pub struct FlatChan<T, F, C> { - flattener: F, - byte_chan: C -} - -/** -Constructors for flat pipes that using serialization-based flattening. -*/ -pub mod serial { - pub use DefaultEncoder = ebml::writer::Encoder; - pub use DefaultDecoder = ebml::reader::Decoder; - - use serialize::{Decodable, Encodable}; - use flatpipes::flatteners::{DeserializingUnflattener, - SerializingFlattener}; - use flatpipes::flatteners::{deserialize_buffer, serialize_value}; - use flatpipes::bytepipes::{ReaderBytePort, WriterByteChan}; - use flatpipes::bytepipes::{PipeBytePort, PipeByteChan}; - use flatpipes::{FlatPort, FlatChan}; - - use core::io::{Reader, Writer}; - use core::comm::{Port, Chan}; - use core::comm; - - pub type ReaderPort<T, R> = FlatPort< - T, DeserializingUnflattener<DefaultDecoder, T>, - ReaderBytePort<R>>; - pub type WriterChan<T, W> = FlatChan< - T, SerializingFlattener<DefaultEncoder, T>, WriterByteChan<W>>; - pub type PipePort<T> = FlatPort< - T, DeserializingUnflattener<DefaultDecoder, T>, PipeBytePort>; - pub type PipeChan<T> = FlatChan< - T, SerializingFlattener<DefaultEncoder, T>, PipeByteChan>; - - /// Create a `FlatPort` from a `Reader` - pub fn reader_port<T: Decodable<DefaultDecoder>, - R: Reader>(reader: R) -> ReaderPort<T, R> { - let unflat: DeserializingUnflattener<DefaultDecoder, T> = - DeserializingUnflattener::new( - deserialize_buffer::<DefaultDecoder, T>); - let byte_port = ReaderBytePort::new(reader); - FlatPort::new(unflat, byte_port) - } - - /// Create a `FlatChan` from a `Writer` - pub fn writer_chan<T: Encodable<DefaultEncoder>, - W: Writer>(writer: W) -> WriterChan<T, W> { - let flat: SerializingFlattener<DefaultEncoder, T> = - SerializingFlattener::new( - serialize_value::<DefaultEncoder, T>); - let byte_chan = WriterByteChan::new(writer); - FlatChan::new(flat, byte_chan) - } - - /// Create a `FlatPort` from a `Port<~[u8]>` - pub fn pipe_port<T:Decodable<DefaultDecoder>>( - port: Port<~[u8]> - ) -> PipePort<T> { - let unflat: DeserializingUnflattener<DefaultDecoder, T> = - DeserializingUnflattener::new( - deserialize_buffer::<DefaultDecoder, T>); - let byte_port = PipeBytePort::new(port); - FlatPort::new(unflat, byte_port) - } - - /// Create a `FlatChan` from a `Chan<~[u8]>` - pub fn pipe_chan<T:Encodable<DefaultEncoder>>( - chan: Chan<~[u8]> - ) -> PipeChan<T> { - let flat: SerializingFlattener<DefaultEncoder, T> = - SerializingFlattener::new( - serialize_value::<DefaultEncoder, T>); - let byte_chan = PipeByteChan::new(chan); - FlatChan::new(flat, byte_chan) - } - - /// Create a pair of `FlatChan` and `FlatPort`, backed by pipes - pub fn pipe_stream<T: Encodable<DefaultEncoder> + - Decodable<DefaultDecoder>>( - ) -> (PipePort<T>, PipeChan<T>) { - let (port, chan) = comm::stream(); - return (pipe_port(port), pipe_chan(chan)); - } -} - -// FIXME #4074 this doesn't correctly enforce POD bounds -/** -Constructors for flat pipes that send POD types using memcpy. - -# Safety Note - -This module is currently unsafe because it uses `Copy Owned` as a type -parameter bounds meaning POD (plain old data), but `Copy Owned` and -POD are not equivelant. - -*/ -pub mod pod { - use flatpipes::flatteners::{PodUnflattener, PodFlattener}; - use flatpipes::bytepipes::{ReaderBytePort, WriterByteChan}; - use flatpipes::bytepipes::{PipeBytePort, PipeByteChan}; - use flatpipes::{FlatPort, FlatChan}; - - use core::io::{Reader, Writer}; - use core::comm::{Port, Chan}; - use core::comm; - - pub type ReaderPort<T, R> = - FlatPort<T, PodUnflattener<T>, ReaderBytePort<R>>; - pub type WriterChan<T, W> = - FlatChan<T, PodFlattener<T>, WriterByteChan<W>>; - pub type PipePort<T> = FlatPort<T, PodUnflattener<T>, PipeBytePort>; - pub type PipeChan<T> = FlatChan<T, PodFlattener<T>, PipeByteChan>; - - /// Create a `FlatPort` from a `Reader` - pub fn reader_port<T:Copy + Owned,R:Reader>( - reader: R - ) -> ReaderPort<T, R> { - let unflat: PodUnflattener<T> = PodUnflattener::new(); - let byte_port = ReaderBytePort::new(reader); - FlatPort::new(unflat, byte_port) - } - - /// Create a `FlatChan` from a `Writer` - pub fn writer_chan<T:Copy + Owned,W:Writer>( - writer: W - ) -> WriterChan<T, W> { - let flat: PodFlattener<T> = PodFlattener::new(); - let byte_chan = WriterByteChan::new(writer); - FlatChan::new(flat, byte_chan) - } - - /// Create a `FlatPort` from a `Port<~[u8]>` - pub fn pipe_port<T:Copy + Owned>(port: Port<~[u8]>) -> PipePort<T> { - let unflat: PodUnflattener<T> = PodUnflattener::new(); - let byte_port = PipeBytePort::new(port); - FlatPort::new(unflat, byte_port) - } - - /// Create a `FlatChan` from a `Chan<~[u8]>` - pub fn pipe_chan<T:Copy + Owned>(chan: Chan<~[u8]>) -> PipeChan<T> { - let flat: PodFlattener<T> = PodFlattener::new(); - let byte_chan = PipeByteChan::new(chan); - FlatChan::new(flat, byte_chan) - } - - /// Create a pair of `FlatChan` and `FlatPort`, backed by pipes - pub fn pipe_stream<T:Copy + Owned>() -> (PipePort<T>, PipeChan<T>) { - let (port, chan) = comm::stream(); - return (pipe_port(port), pipe_chan(chan)); - } - -} - -/** -Flatteners present a value as a byte vector -*/ -pub trait Flattener<T> { - fn flatten(&self, val: T) -> ~[u8]; -} - -/** -Unflatteners convert a byte vector to a value -*/ -pub trait Unflattener<T> { - fn unflatten(&self, buf: ~[u8]) -> T; -} - -/** -BytePorts are a simple interface for receiving a specified number -*/ -pub trait BytePort { - fn try_recv(&self, count: uint) -> Option<~[u8]>; -} - -/** -ByteChans are a simple interface for sending bytes -*/ -pub trait ByteChan { - fn send(&self, val: ~[u8]); -} - -static CONTINUE: [u8, ..4] = [0xAA, 0xBB, 0xCC, 0xDD]; - -impl<T,U:Unflattener<T>,P:BytePort> GenericPort<T> for FlatPort<T, U, P> { - fn recv(&self) -> T { - match self.try_recv() { - Some(val) => val, - None => fail!("port is closed") - } - } - fn try_recv(&self) -> Option<T> { - let command = match self.byte_port.try_recv(CONTINUE.len()) { - Some(c) => c, - None => { - warn!("flatpipe: broken pipe"); - return None; - } - }; - - if vec::eq(command, CONTINUE) { - let msg_len = match self.byte_port.try_recv(size_of::<u64>()) { - Some(bytes) => { - io::u64_from_be_bytes(bytes, 0, size_of::<u64>()) - }, - None => { - warn!("flatpipe: broken pipe"); - return None; - } - }; - - let msg_len = msg_len as uint; - - match self.byte_port.try_recv(msg_len) { - Some(bytes) => { - Some(self.unflattener.unflatten(bytes)) - } - None => { - warn!("flatpipe: broken pipe"); - return None; - } - } - } - else { - fail!("flatpipe: unrecognized command"); - } - } -} - -impl<T,F:Flattener<T>,C:ByteChan> GenericChan<T> for FlatChan<T, F, C> { - fn send(&self, val: T) { - self.byte_chan.send(CONTINUE.to_vec()); - let bytes = self.flattener.flatten(val); - let len = bytes.len() as u64; - do io::u64_to_be_bytes(len, size_of::<u64>()) |len_bytes| { - self.byte_chan.send(len_bytes.to_vec()); - } - self.byte_chan.send(bytes); - } -} - -pub impl<T,U:Unflattener<T>,P:BytePort> FlatPort<T, U, P> { - fn new(u: U, p: P) -> FlatPort<T, U, P> { - FlatPort { - unflattener: u, - byte_port: p - } - } -} - -pub impl<T,F:Flattener<T>,C:ByteChan> FlatChan<T, F, C> { - fn new(f: F, c: C) -> FlatChan<T, F, C> { - FlatChan { - flattener: f, - byte_chan: c - } - } -} - - -pub mod flatteners { - use ebml; - use flatpipes::{Flattener, Unflattener}; - use io_util::BufReader; - use json; - use serialize::{Encoder, Decoder, Encodable, Decodable}; - - use core::cast; - use core::io::{Writer, Reader, ReaderUtil}; - use core::ptr; - use core::sys::size_of; - use core::vec; - - - // FIXME #4074: Copy + Owned != POD - pub struct PodUnflattener<T> { - bogus: () - } - - pub struct PodFlattener<T> { - bogus: () - } - - impl<T:Copy + Owned> Unflattener<T> for PodUnflattener<T> { - fn unflatten(&self, buf: ~[u8]) -> T { - assert!(size_of::<T>() != 0); - assert_eq!(size_of::<T>(), buf.len()); - let addr_of_init: &u8 = unsafe { &*vec::raw::to_ptr(buf) }; - let addr_of_value: &T = unsafe { cast::transmute(addr_of_init) }; - copy *addr_of_value - } - } - - impl<T:Copy + Owned> Flattener<T> for PodFlattener<T> { - fn flatten(&self, val: T) -> ~[u8] { - assert!(size_of::<T>() != 0); - let val: *T = ptr::to_unsafe_ptr(&val); - let byte_value = val as *u8; - unsafe { vec::from_buf(byte_value, size_of::<T>()) } - } - } - - pub impl<T:Copy + Owned> PodUnflattener<T> { - fn new() -> PodUnflattener<T> { - PodUnflattener { - bogus: () - } - } - } - - pub impl<T:Copy + Owned> PodFlattener<T> { - fn new() -> PodFlattener<T> { - PodFlattener { - bogus: () - } - } - } - - - pub type DeserializeBuffer<T> = ~fn(buf: &[u8]) -> T; - - pub struct DeserializingUnflattener<D, T> { - deserialize_buffer: DeserializeBuffer<T> - } - - pub type SerializeValue<T> = ~fn(val: &T) -> ~[u8]; - - pub struct SerializingFlattener<S, T> { - serialize_value: SerializeValue<T> - } - - impl<D:Decoder,T:Decodable<D>> Unflattener<T> - for DeserializingUnflattener<D, T> { - fn unflatten(&self, buf: ~[u8]) -> T { - (self.deserialize_buffer)(buf) - } - } - - impl<S:Encoder,T:Encodable<S>> Flattener<T> - for SerializingFlattener<S, T> { - fn flatten(&self, val: T) -> ~[u8] { - (self.serialize_value)(&val) - } - } - - pub impl<D:Decoder,T:Decodable<D>> DeserializingUnflattener<D, T> { - fn new(deserialize_buffer: DeserializeBuffer<T>) - -> DeserializingUnflattener<D, T> { - DeserializingUnflattener { - deserialize_buffer: deserialize_buffer - } - } - } - - pub impl<S:Encoder,T:Encodable<S>> SerializingFlattener<S, T> { - fn new(serialize_value: SerializeValue<T>) - -> SerializingFlattener<S, T> { - SerializingFlattener { - serialize_value: serialize_value - } - } - } - - /* - Implementations of the serialization functions required by - SerializingFlattener - */ - - pub fn deserialize_buffer<D: Decoder + FromReader, - T: Decodable<D>>( - buf: &[u8]) - -> T { - let buf = vec::to_owned(buf); - let buf_reader = @BufReader::new(buf); - let reader = buf_reader as @Reader; - let mut deser: D = FromReader::from_reader(reader); - Decodable::decode(&mut deser) - } - - pub fn serialize_value<D: Encoder + FromWriter, - T: Encodable<D>>( - val: &T) - -> ~[u8] { - do io::with_bytes_writer |writer| { - let mut ser = FromWriter::from_writer(writer); - val.encode(&mut ser); - } - } - - pub trait FromReader { - fn from_reader(r: @Reader) -> Self; - } - - pub trait FromWriter { - fn from_writer(w: @Writer) -> Self; - } - - impl FromReader for json::Decoder { - fn from_reader(r: @Reader) -> json::Decoder { - match json::from_reader(r) { - Ok(json) => { - json::Decoder(json) - } - Err(e) => fail!("flatpipe: can't parse json: %?", e) - } - } - } - - impl FromWriter for json::Encoder { - fn from_writer(w: @Writer) -> json::Encoder { - json::Encoder(w) - } - } - - impl FromReader for ebml::reader::Decoder { - fn from_reader(r: @Reader) -> ebml::reader::Decoder { - let buf = @r.read_whole_stream(); - let doc = ebml::reader::Doc(buf); - ebml::reader::Decoder(doc) - } - } - - impl FromWriter for ebml::writer::Encoder { - fn from_writer(w: @Writer) -> ebml::writer::Encoder { - ebml::writer::Encoder(w) - } - } - -} - -pub mod bytepipes { - use flatpipes::{ByteChan, BytePort}; - - use core::io::{Writer, Reader, ReaderUtil}; - use core::comm::{Port, Chan}; - - pub struct ReaderBytePort<R> { - reader: R - } - - pub struct WriterByteChan<W> { - writer: W - } - - impl<R:Reader> BytePort for ReaderBytePort<R> { - fn try_recv(&self, count: uint) -> Option<~[u8]> { - let mut left = count; - let mut bytes = ~[]; - while !self.reader.eof() && left > 0 { - assert!(left <= count); - assert!(left > 0); - let new_bytes = self.reader.read_bytes(left); - bytes.push_all(new_bytes); - assert!(new_bytes.len() <= left); - left -= new_bytes.len(); - } - - if left == 0 { - return Some(bytes); - } else { - warn!("flatpipe: dropped %? broken bytes", left); - return None; - } - } - } - - impl<W:Writer> ByteChan for WriterByteChan<W> { - fn send(&self, val: ~[u8]) { - self.writer.write(val); - } - } - - pub impl<R:Reader> ReaderBytePort<R> { - fn new(r: R) -> ReaderBytePort<R> { - ReaderBytePort { - reader: r - } - } - } - - pub impl<W:Writer> WriterByteChan<W> { - fn new(w: W) -> WriterByteChan<W> { - WriterByteChan { - writer: w - } - } - } - - // XXX: Remove `@mut` when this module is ported to the new I/O traits, - // which use `&mut self` properly. - pub struct PipeBytePort { - port: comm::Port<~[u8]>, - buf: @mut ~[u8] - } - - pub struct PipeByteChan { - chan: comm::Chan<~[u8]> - } - - impl BytePort for PipeBytePort { - fn try_recv(&self, count: uint) -> Option<~[u8]> { - if vec::uniq_len(&const *self.buf) >= count { - let mut bytes = ::core::util::replace(&mut *self.buf, ~[]); - *self.buf = bytes.slice(count, bytes.len()).to_owned(); - bytes.truncate(count); - return Some(bytes); - } else if vec::uniq_len(&const *self.buf) > 0 { - let mut bytes = ::core::util::replace(&mut *self.buf, ~[]); - assert!(count > bytes.len()); - match self.try_recv(count - bytes.len()) { - Some(rest) => { - bytes.push_all(rest); - return Some(bytes); - } - None => return None - } - } else if vec::uniq_len(&const *self.buf) == 0 { - match self.port.try_recv() { - Some(buf) => { - assert!(!buf.is_empty()); - *self.buf = buf; - return self.try_recv(count); - } - None => return None - } - } else { - ::core::util::unreachable() - } - } - } - - impl ByteChan for PipeByteChan { - fn send(&self, val: ~[u8]) { - self.chan.send(val) - } - } - - pub impl PipeBytePort { - fn new(p: Port<~[u8]>) -> PipeBytePort { - PipeBytePort { - port: p, - buf: @mut ~[] - } - } - } - - pub impl PipeByteChan { - fn new(c: Chan<~[u8]>) -> PipeByteChan { - PipeByteChan { - chan: c - } - } - } - -} - -#[cfg(test)] -mod test { - use flatpipes::{Flattener, Unflattener}; - use flatpipes::bytepipes::*; - use flatpipes::pod; - use flatpipes::serial; - use io_util::BufReader; - use flatpipes::{BytePort, FlatChan, FlatPort}; - use net::tcp::TcpSocketBuf; - - use core::io::BytesWriter; - - #[test] - #[ignore(reason = "ebml failure")] - fn test_serializing_memory_stream() { - let writer = BytesWriter(); - let chan = serial::writer_chan(writer); - - chan.send(10); - - let bytes = copy *chan.byte_chan.writer.bytes; - - let reader = BufReader::new(bytes); - let port = serial::reader_port(reader); - - let res: int = port.recv(); - assert_eq!(res, 10i); - } - - #[test] - #[ignore(reason = "FIXME #6211 failing on linux snapshot machine")] - fn test_serializing_pipes() { - let (port, chan) = serial::pipe_stream(); - - do task::spawn || { - for int::range(0, 10) |i| { - chan.send(i) - } - } - - for int::range(0, 10) |i| { - assert!(i == port.recv()) - } - } - - #[test] - #[ignore(reason = "ebml failure")] - fn test_serializing_boxes() { - let (port, chan) = serial::pipe_stream(); - - do task::spawn || { - for int::range(0, 10) |i| { - chan.send(@i) - } - } - - for int::range(0, 10) |i| { - assert!(@i == port.recv()) - } - } - - #[test] - fn test_pod_memory_stream() { - let writer = BytesWriter(); - let chan = pod::writer_chan(writer); - - chan.send(10); - - let bytes = copy *chan.byte_chan.writer.bytes; - - let reader = BufReader::new(bytes); - let port = pod::reader_port(reader); - - let res: int = port.recv(); - assert_eq!(res, 10); - } - - #[test] - fn test_pod_pipes() { - let (port, chan) = pod::pipe_stream(); - - do task::spawn || { - for int::range(0, 10) |i| { - chan.send(i) - } - } - - for int::range(0, 10) |i| { - assert!(i == port.recv()) - } - } - - // FIXME #2064: Networking doesn't work on x86 - #[test] - #[cfg(target_arch = "x86_64")] - fn test_pod_tcp_stream() { - fn reader_port(buf: TcpSocketBuf - ) -> pod::ReaderPort<int, TcpSocketBuf> { - pod::reader_port(buf) - } - fn writer_chan(buf: TcpSocketBuf - ) -> pod::WriterChan<int, TcpSocketBuf> { - pod::writer_chan(buf) - } - test_some_tcp_stream(reader_port, writer_chan, 9666); - } - - #[test] - #[cfg(target_arch = "x86_64")] - fn test_serializing_tcp_stream() { - fn reader_port(buf: TcpSocketBuf - ) -> serial::ReaderPort<int, TcpSocketBuf> { - serial::reader_port(buf) - } - fn writer_chan(buf: TcpSocketBuf - ) -> serial::WriterChan<int, TcpSocketBuf> { - serial::writer_chan(buf) - } - test_some_tcp_stream(reader_port, writer_chan, 9667); - } - - type ReaderPortFactory<U> = - ~fn(TcpSocketBuf) -> FlatPort<int, U, ReaderBytePort<TcpSocketBuf>>; - type WriterChanFactory<F> = - ~fn(TcpSocketBuf) -> FlatChan<int, F, WriterByteChan<TcpSocketBuf>>; - - fn test_some_tcp_stream<U:Unflattener<int>,F:Flattener<int>>( - reader_port: ReaderPortFactory<U>, - writer_chan: WriterChanFactory<F>, - port: uint) { - - use core::cell::Cell; - use net::ip; - use net::tcp; - use uv; - - // Indicate to the client task that the server is listening - let (begin_connect_port, begin_connect_chan) = comm::stream(); - // The connection is sent from the server task to the receiver task - // to handle the connection - let (accept_port, accept_chan) = comm::stream(); - // The main task will wait until the test is over to proceed - let (finish_port, finish_chan) = comm::stream(); - - let addr0 = ip::v4::parse_addr("127.0.0.1"); - - let begin_connect_chan = Cell(begin_connect_chan); - let accept_chan = Cell(accept_chan); - - // The server task - let addr = copy addr0; - do task::spawn || { - let iotask = &uv::global_loop::get(); - let begin_connect_chan = begin_connect_chan.take(); - let accept_chan = accept_chan.take(); - let listen_res = do tcp::listen( - copy addr, port, 128, iotask, |_kill_ch| { - // Tell the sender to initiate the connection - debug!("listening"); - begin_connect_chan.send(()) - }) |new_conn, kill_ch| { - - // Incoming connection. Send it to the receiver task to accept - let (res_port, res_chan) = comm::stream(); - accept_chan.send((new_conn, res_chan)); - // Wait until the connection is accepted - res_port.recv(); - - // Stop listening - kill_ch.send(None) - }; - - assert!(listen_res.is_ok()); - } - - // Client task - let addr = copy addr0; - do task::spawn || { - // Wait for the server to start listening - begin_connect_port.recv(); - - debug!("connecting"); - let iotask = &uv::global_loop::get(); - let connect_result = tcp::connect(copy addr, port, iotask); - assert!(connect_result.is_ok()); - let sock = result::unwrap(connect_result); - let socket_buf: tcp::TcpSocketBuf = tcp::socket_buf(sock); - - // TcpSocketBuf is a Writer! - let chan = writer_chan(socket_buf); - - for int::range(0, 10) |i| { - debug!("sending %?", i); - chan.send(i) - } - } - - // Receiver task - do task::spawn || { - // Wait for a connection - let (conn, res_chan) = accept_port.recv(); - - debug!("accepting connection"); - let accept_result = tcp::accept(conn); - debug!("accepted"); - assert!(accept_result.is_ok()); - let sock = result::unwrap(accept_result); - res_chan.send(()); - - let socket_buf: tcp::TcpSocketBuf = tcp::socket_buf(sock); - - // TcpSocketBuf is a Reader! - let port = reader_port(socket_buf); - - for int::range(0, 10) |i| { - let j = port.recv(); - debug!("received %?", j); - assert_eq!(i, j); - } - - // The test is over! - finish_chan.send(()); - } - - finish_port.recv(); - } - - // Tests that the different backends behave the same when the - // binary streaming protocol is broken - mod broken_protocol { - use flatpipes::{BytePort, FlatPort}; - use flatpipes::flatteners::PodUnflattener; - use flatpipes::pod; - use io_util::BufReader; - - type PortLoader<P> = - ~fn(~[u8]) -> FlatPort<int, PodUnflattener<int>, P>; - - fn reader_port_loader(bytes: ~[u8] - ) -> pod::ReaderPort<int, BufReader> { - let reader = BufReader::new(bytes); - pod::reader_port(reader) - } - - fn pipe_port_loader(bytes: ~[u8] - ) -> pod::PipePort<int> { - let (port, chan) = comm::stream(); - if !bytes.is_empty() { - chan.send(bytes); - } - pod::pipe_port(port) - } - - fn test_try_recv_none1<P:BytePort>(loader: PortLoader<P>) { - let bytes = ~[]; - let port = loader(bytes); - let res: Option<int> = port.try_recv(); - assert!(res.is_none()); - } - - #[test] - fn test_try_recv_none1_reader() { - test_try_recv_none1(reader_port_loader); - } - #[test] - fn test_try_recv_none1_pipe() { - test_try_recv_none1(pipe_port_loader); - } - - fn test_try_recv_none2<P:BytePort>(loader: PortLoader<P>) { - // The control word in the protocol is interrupted - let bytes = ~[0]; - let port = loader(bytes); - let res: Option<int> = port.try_recv(); - assert!(res.is_none()); - } - - #[test] - fn test_try_recv_none2_reader() { - test_try_recv_none2(reader_port_loader); - } - #[test] - fn test_try_recv_none2_pipe() { - test_try_recv_none2(pipe_port_loader); - } - - fn test_try_recv_none3<P:BytePort>(loader: PortLoader<P>) { - static CONTINUE: [u8, ..4] = [0xAA, 0xBB, 0xCC, 0xDD]; - // The control word is followed by garbage - let bytes = CONTINUE.to_vec() + ~[0]; - let port = loader(bytes); - let res: Option<int> = port.try_recv(); - assert!(res.is_none()); - } - - #[test] - fn test_try_recv_none3_reader() { - test_try_recv_none3(reader_port_loader); - } - #[test] - fn test_try_recv_none3_pipe() { - test_try_recv_none3(pipe_port_loader); - } - - fn test_try_recv_none4<P:BytePort>(loader: PortLoader<P>) { - assert!(do task::try || { - static CONTINUE: [u8, ..4] = [0xAA, 0xBB, 0xCC, 0xDD]; - // The control word is followed by a valid length, - // then undeserializable garbage - let len_bytes = do io::u64_to_be_bytes( - 1, sys::size_of::<u64>()) |len_bytes| { - len_bytes.to_vec() - }; - let bytes = CONTINUE.to_vec() + len_bytes + ~[0, 0, 0, 0]; - - let port = loader(bytes); - - let _res: Option<int> = port.try_recv(); - }.is_err()); - } - - #[test] - #[ignore(cfg(windows))] - fn test_try_recv_none4_reader() { - test_try_recv_none4(reader_port_loader); - } - #[test] - #[ignore(cfg(windows))] - fn test_try_recv_none4_pipe() { - test_try_recv_none4(pipe_port_loader); - } - } - -} diff --git a/src/libstd/net.rs b/src/libstd/from_str.rs index ca1cc123596..ebf6d212466 100644 --- a/src/libstd/net.rs +++ b/src/libstd/from_str.rs @@ -8,18 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! -Top-level module for network-related functionality. +//! The trait for types that can be created from strings -Basically, including this module gives you: +use option::Option; -* `net_tcp` -* `net_ip` -* `net_url` - -See each of those three modules for documentation on what they do. -*/ - -pub use tcp = net_tcp; -pub use ip = net_ip; -pub use url = net_url; +pub trait FromStr { + fn from_str(s: &str) -> Option<Self>; +} diff --git a/src/libstd/fun_treemap.rs b/src/libstd/fun_treemap.rs deleted file mode 100644 index 6b051fa21b1..00000000000 --- a/src/libstd/fun_treemap.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * A functional key,value store that works on anything. - * - * This works using a binary search tree. In the first version, it's a - * very naive algorithm, but it will probably be updated to be a - * red-black tree or something else. - * - * This is copied and modified from treemap right now. It's missing a lot - * of features. - */ - -use core::cmp::{Eq, Ord}; -use core::option::{Some, None}; - -pub type Treemap<K, V> = @TreeNode<K, V>; - -enum TreeNode<K, V> { - Empty, - Node(@K, @V, @TreeNode<K, V>, @TreeNode<K, V>) -} - -/// Create a treemap -pub fn init<K, V>() -> Treemap<K, V> { @Empty } - -/// Insert a value into the map -pub fn insert<K:Copy + Eq + Ord,V:Copy>(m: Treemap<K, V>, k: K, v: V) -> Treemap<K, V> { - @match m { - @Empty => Node(@k, @v, @Empty, @Empty), - @Node(@copy kk, vv, left, right) => cond!( - (k < kk) { Node(@kk, vv, insert(left, k, v), right) } - (k == kk) { Node(@kk, @v, left, right) } - _ { Node(@kk, vv, left, insert(right, k, v)) } - ) - } -} - -/// Find a value based on the key -pub fn find<K:Eq + Ord,V:Copy>(m: Treemap<K, V>, k: K) -> Option<V> { - match *m { - Empty => None, - Node(@ref kk, @copy v, left, right) => cond!( - (k == *kk) { Some(v) } - (k < *kk) { find(left, k) } - _ { find(right, k) } - ) - } -} - -/// Visit all pairs in the map in order. -pub fn traverse<K, V: Copy>(m: Treemap<K, V>, f: &fn(&K, &V)) { - match *m { - Empty => (), - // Previously, this had what looked like redundant - // matches to me, so I changed it. but that may be a - // de-optimization -- tjc - Node(@ref k, @ref v, left, right) => { - traverse(left, f); - f(k, v); - traverse(right, f); - } - } -} diff --git a/src/libstd/future.rs b/src/libstd/future.rs deleted file mode 100644 index 02828b8eaee..00000000000 --- a/src/libstd/future.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * A type representing values that may be computed concurrently and - * operations for working with them. - * - * # Example - * - * ~~~ - * # fn fib(n: uint) -> uint {42}; - * # fn make_a_sandwich() {}; - * let mut delayed_fib = std::future::spawn (|| fib(5000) ); - * make_a_sandwich(); - * println(fmt!("fib(5000) = %?", delayed_fib.get())) - * ~~~ - */ - -use core::cast; -use core::cell::Cell; -use core::comm::{PortOne, oneshot, send_one, recv_one}; -use core::task; -use core::util::replace; - -#[doc = "The future type"] -pub struct Future<A> { - priv state: FutureState<A>, -} - -// n.b. It should be possible to get rid of this. -// Add a test, though -- tjc -// FIXME(#2829) -- futures should not be copyable, because they close -// over ~fn's that have pipes and so forth within! -#[unsafe_destructor] -impl<A> Drop for Future<A> { - fn finalize(&self) {} -} - -priv enum FutureState<A> { - Pending(~fn() -> A), - Evaluating, - Forced(A) -} - -/// Methods on the `future` type -pub impl<A:Copy> Future<A> { - fn get(&mut self) -> A { - //! Get the value of the future. - *(self.get_ref()) - } -} - -pub impl<A> Future<A> { - fn get_ref<'a>(&'a mut self) -> &'a A { - /*! - * Executes the future's closure and then returns a borrowed - * pointer to the result. The borrowed pointer lasts as long as - * the future. - */ - unsafe { - { - match self.state { - Forced(ref mut v) => { return cast::transmute(v); } - Evaluating => fail!("Recursive forcing of future!"), - Pending(_) => {} - } - } - { - let state = replace(&mut self.state, Evaluating); - match state { - Forced(_) | Evaluating => fail!("Logic error."), - Pending(f) => { - self.state = Forced(f()); - cast::transmute(self.get_ref()) - } - } - } - } - } -} - -pub fn from_value<A>(val: A) -> Future<A> { - /*! - * Create a future from a value. - * - * The value is immediately available and calling `get` later will - * not block. - */ - - Future {state: Forced(val)} -} - -pub fn from_port<A:Owned>(port: PortOne<A>) -> Future<A> { - /*! - * Create a future from a port - * - * The first time that the value is requested the task will block - * waiting for the result to be received on the port. - */ - - let port = Cell(port); - do from_fn { - recv_one(port.take()) - } -} - -pub fn from_fn<A>(f: ~fn() -> A) -> Future<A> { - /*! - * Create a future from a function. - * - * The first time that the value is requested it will be retrieved by - * calling the function. Note that this function is a local - * function. It is not spawned into another task. - */ - - Future {state: Pending(f)} -} - -pub fn spawn<A:Owned>(blk: ~fn() -> A) -> Future<A> { - /*! - * Create a future from a unique closure. - * - * The closure will be run in a new task and its result used as the - * value of the future. - */ - - let (port, chan) = oneshot(); - - let chan = Cell(chan); - do task::spawn { - let chan = chan.take(); - send_one(chan, blk()); - } - - return from_port(port); -} - -#[allow(non_implicitly_copyable_typarams)] -#[cfg(test)] -mod test { - use future::*; - - use core::cell::Cell; - use core::comm::{oneshot, send_one}; - use core::task; - - #[test] - fn test_from_value() { - let mut f = from_value(~"snail"); - assert_eq!(f.get(), ~"snail"); - } - - #[test] - fn test_from_port() { - let (po, ch) = oneshot(); - send_one(ch, ~"whale"); - let mut f = from_port(po); - assert_eq!(f.get(), ~"whale"); - } - - #[test] - fn test_from_fn() { - let mut f = from_fn(|| ~"brail"); - assert_eq!(f.get(), ~"brail"); - } - - #[test] - fn test_interface_get() { - let mut f = from_value(~"fail"); - assert_eq!(f.get(), ~"fail"); - } - - #[test] - fn test_get_ref_method() { - let mut f = from_value(22); - assert_eq!(*f.get_ref(), 22); - } - - #[test] - fn test_spawn() { - let mut f = spawn(|| ~"bale"); - assert_eq!(f.get(), ~"bale"); - } - - #[test] - #[should_fail] - #[ignore(cfg(target_os = "win32"))] - fn test_futurefail() { - let mut f = spawn(|| fail!()); - let _x: ~str = f.get(); - } - - #[test] - fn test_sendable_future() { - let expected = "schlorf"; - let f = Cell(do spawn { expected }); - do task::spawn { - let mut f = f.take(); - let actual = f.get(); - assert_eq!(actual, expected); - } - } -} diff --git a/src/libstd/gc.rs b/src/libstd/gc.rs new file mode 100644 index 00000000000..611b95a7745 --- /dev/null +++ b/src/libstd/gc.rs @@ -0,0 +1,371 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; + +/*! Precise garbage collector + +The precise GC exposes two functions, gc and +cleanup_stack_for_failure. The gc function is the entry point to the +garbage collector itself. The cleanup_stack_for_failure is the entry +point for GC-based cleanup. + +Precise GC depends on changes to LLVM's GC which add support for +automatic rooting and addrspace-based metadata marking. Rather than +explicitly rooting pointers with LLVM's gcroot intrinsic, the GC +merely creates allocas for pointers, and allows an LLVM pass to +automatically infer roots based on the allocas present in a function +(and live at a given location). The compiler communicates the type of +the pointer to LLVM by setting the addrspace of the pointer type. The +compiler then emits a map from addrspace to tydesc, which LLVM then +uses to match pointers with their tydesc. The GC reads the metadata +table produced by LLVM, and uses it to determine which glue functions +to call to free objects on their respective heaps. + +GC-based cleanup is a replacement for landing pads which relies on the +GC infrastructure to find pointers on the stack to cleanup. Whereas +the normal GC needs to walk task-local heap allocations, the cleanup +code needs to walk exchange heap allocations and stack-allocations +with destructors. + +*/ + +use cast; +use container::{Map, Set}; +use io; +use libc::{size_t, uintptr_t}; +use option::{None, Option, Some}; +use ptr; +use hashmap::HashSet; +use stackwalk::walk_stack; +use sys; + +pub use stackwalk::Word; + +// Mirrors rust_stack.h stk_seg +pub struct StackSegment { + prev: *StackSegment, + next: *StackSegment, + end: uintptr_t, + // And other fields which we don't care about... +} + +pub mod rustrt { + use libc::size_t; + use stackwalk::Word; + use super::StackSegment; + + #[link_name = "rustrt"] + pub extern { + #[rust_stack] + pub unsafe fn rust_call_tydesc_glue(root: *Word, + tydesc: *Word, + field: size_t); + + #[rust_stack] + pub unsafe fn rust_gc_metadata() -> *Word; + + pub unsafe fn rust_get_stack_segment() -> *StackSegment; + pub unsafe fn rust_get_c_stack() -> *StackSegment; + } +} + +unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U { + return cast::transmute(ptr::offset(ptr, count)); +} + +unsafe fn align_to_pointer<T>(ptr: *T) -> *T { + let align = sys::min_align_of::<*T>(); + let ptr: uint = cast::transmute(ptr); + let ptr = (ptr + (align - 1)) & -align; + return cast::transmute(ptr); +} + +unsafe fn get_safe_point_count() -> uint { + let module_meta = rustrt::rust_gc_metadata(); + return *module_meta; +} + +struct SafePoint { + sp_meta: *Word, + fn_meta: *Word, +} + +// Returns the safe point metadata for the given program counter, if +// any. +unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> { + let module_meta = rustrt::rust_gc_metadata(); + let num_safe_points = *module_meta; + let safe_points: *Word = bump(module_meta, 1); + + if ptr::is_null(pc) { + return None; + } + + // FIXME (#2997): Use binary rather than linear search. + let mut spi = 0; + while spi < num_safe_points { + let sp: **Word = bump(safe_points, spi*3); + let sp_loc = *sp; + if sp_loc == pc { + return Some(SafePoint { + sp_meta: *bump(sp, 1), + fn_meta: *bump(sp, 2), + }); + } + spi += 1; + } + return None; +} + +type Visitor<'self> = &'self fn(root: **Word, tydesc: *Word) -> bool; + +// Walks the list of roots for the given safe point, and calls visitor +// on each root. +unsafe fn _walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) -> bool { + let fp_bytes: *u8 = cast::transmute(fp); + let sp_meta: *u32 = cast::transmute(sp.sp_meta); + + let num_stack_roots = *sp_meta as uint; + let num_reg_roots = *ptr::offset(sp_meta, 1) as uint; + + let stack_roots: *u32 = bump(sp_meta, 2); + let reg_roots: *u8 = bump(stack_roots, num_stack_roots); + let addrspaces: *Word = align_to_pointer(bump(reg_roots, num_reg_roots)); + let tydescs: ***Word = bump(addrspaces, num_stack_roots); + + // Stack roots + let mut sri = 0; + while sri < num_stack_roots { + if *ptr::offset(addrspaces, sri) >= 1 { + let root = + ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri) as Word) + as **Word; + let tydescpp = ptr::offset(tydescs, sri); + let tydesc = if ptr::is_not_null(tydescpp) && + ptr::is_not_null(*tydescpp) { + **tydescpp + } else { + ptr::null() + }; + if !visitor(root, tydesc) { return false; } + } + sri += 1; + } + + // Register roots + let mut rri = 0; + while rri < num_reg_roots { + if *ptr::offset(addrspaces, num_stack_roots + rri) == 1 { + // FIXME(#2997): Need to find callee saved registers on the stack. + } + rri += 1; + } + return true; +} + +unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) -> bool { + _walk_safe_point(fp, sp, visitor) +} + +// Is fp contained in segment? +unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool { + let begin: Word = cast::transmute(segment); + let end: Word = cast::transmute((*segment).end); + let frame: Word = cast::transmute(fp); + + return begin <= frame && frame <= end; +} + +struct Segment { segment: *StackSegment, boundary: bool } + +// Find and return the segment containing the given frame pointer. At +// stack segment boundaries, returns true for boundary, so that the +// caller can do any special handling to identify where the correct +// return address is in the stack frame. +unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment) + -> Segment { + // Check if frame is in either current frame or previous frame. + let in_segment = is_frame_in_segment(fp, segment); + let in_prev_segment = ptr::is_not_null((*segment).prev) && + is_frame_in_segment(fp, (*segment).prev); + + // If frame is not in either segment, walk down segment list until + // we find the segment containing this frame. + if !in_segment && !in_prev_segment { + let mut segment = segment; + while ptr::is_not_null((*segment).next) && + is_frame_in_segment(fp, (*segment).next) { + segment = (*segment).next; + } + return Segment {segment: segment, boundary: false}; + } + + // If frame is in previous frame, then we're at a boundary. + if !in_segment && in_prev_segment { + return Segment {segment: (*segment).prev, boundary: true}; + } + + // Otherwise, we're somewhere on the inside of the frame. + return Segment {segment: segment, boundary: false}; +} + +type Memory = uint; + +static task_local_heap: Memory = 1; +static exchange_heap: Memory = 2; +static stack: Memory = 4; + +static need_cleanup: Memory = exchange_heap | stack; + +// Walks stack, searching for roots of the requested type, and passes +// each root to the visitor. +unsafe fn _walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) -> bool { + let mut segment = rustrt::rust_get_stack_segment(); + let mut last_ret: *Word = ptr::null(); + // To avoid collecting memory used by the GC itself, skip stack + // frames until past the root GC stack frame. The root GC stack + // frame is marked by a sentinel, which is a box pointer stored on + // the stack. + let mut reached_sentinel = ptr::is_null(sentinel); + for walk_stack |frame| { + let pc = last_ret; + let Segment {segment: next_segment, boundary: boundary} = + find_segment_for_frame(frame.fp, segment); + segment = next_segment; + // Each stack segment is bounded by a morestack frame. The + // morestack frame includes two return addresses, one for + // morestack itself, at the normal offset from the frame + // pointer, and then a second return address for the + // function prologue (which called morestack after + // determining that it had hit the end of the stack). + // Since morestack itself takes two parameters, the offset + // for this second return address is 3 greater than the + // return address for morestack. + let ret_offset = if boundary { 4 } else { 1 }; + last_ret = *ptr::offset(frame.fp, ret_offset) as *Word; + + if ptr::is_null(pc) { + loop; + } + + let mut delay_reached_sentinel = reached_sentinel; + let sp = is_safe_point(pc); + match sp { + Some(sp_info) => { + for walk_safe_point(frame.fp, sp_info) |root, tydesc| { + // Skip roots until we see the sentinel. + if !reached_sentinel { + if root == sentinel { + delay_reached_sentinel = true; + } + loop; + } + + // Skip null pointers, which can occur when a + // unique pointer has already been freed. + if ptr::is_null(*root) { + loop; + } + + if ptr::is_null(tydesc) { + // Root is a generic box. + let refcount = **root; + if mem | task_local_heap != 0 && refcount != -1 { + if !visitor(root, tydesc) { return false; } + } else if mem | exchange_heap != 0 && refcount == -1 { + if !visitor(root, tydesc) { return false; } + } + } else { + // Root is a non-immediate. + if mem | stack != 0 { + if !visitor(root, tydesc) { return false; } + } + } + } + } + None => () + } + reached_sentinel = delay_reached_sentinel; + } + return true; +} + +unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) -> bool { + _walk_gc_roots(mem, sentinel, visitor) +} +pub fn gc() { + unsafe { + // Abort when GC is disabled. + if get_safe_point_count() == 0 { + return; + } + + for walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| { + // FIXME(#2997): Walk roots and mark them. + io::stdout().write([46]); // . + } + } +} + +#[cfg(gc)] +fn expect_sentinel() -> bool { true } + +#[cfg(nogc)] +fn expect_sentinel() -> bool { false } + +// Entry point for GC-based cleanup. Walks stack looking for exchange +// heap and stack allocations requiring drop, and runs all +// destructors. +// +// This should only be called from fail!, as it will drop the roots +// which are *live* on the stack, rather than dropping those that are +// dead. +pub fn cleanup_stack_for_failure() { + unsafe { + // Abort when GC is disabled. + if get_safe_point_count() == 0 { + return; + } + + // Leave a sentinel on the stack to mark the current frame. The + // stack walker will ignore any frames above the sentinel, thus + // avoiding collecting any memory being used by the stack walker + // itself. + // + // However, when core itself is not compiled with GC, then none of + // the functions in core will have GC metadata, which means we + // won't be able to find the sentinel root on the stack. In this + // case, we can safely skip the sentinel since we won't find our + // own stack roots on the stack anyway. + let sentinel_box = ~0; + let sentinel: **Word = if expect_sentinel() { + cast::transmute(&sentinel_box) + } else { + ptr::null() + }; + + let mut roots = HashSet::new(); + for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| { + // Track roots to avoid double frees. + if roots.contains(&*root) { + loop; + } + roots.insert(*root); + + if ptr::is_null(tydesc) { + // FIXME #4420: Destroy this box + // FIXME #4330: Destroy this box + } else { + rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t); + } + } + } +} diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs deleted file mode 100644 index 6a9a60baae6..00000000000 --- a/src/libstd/getopts.rs +++ /dev/null @@ -1,1381 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * Simple getopt alternative. - * - * Construct a vector of options, either by using reqopt, optopt, and optflag - * or by building them from components yourself, and pass them to getopts, - * along with a vector of actual arguments (not including argv[0]). You'll - * either get a failure code back, or a match. You'll have to verify whether - * the amount of 'free' arguments in the match is what you expect. Use opt_* - * accessors to get argument values out of the matches object. - * - * Single-character options are expected to appear on the command line with a - * single preceding dash; multiple-character options are expected to be - * proceeded by two dashes. Options that expect an argument accept their - * argument following either a space or an equals sign. Single-character - * options don't require the space. - * - * # Example - * - * The following example shows simple command line parsing for an application - * that requires an input file to be specified, accepts an optional output - * file name following -o, and accepts both -h and --help as optional flags. - * - * ``` - * extern mod std; - * use std::getopts::*; - * - * fn do_work(in: &str, out: Option<~str>) { - * io::println(in); - * io::println(match out { - * Some(x) => x, - * None => ~"No Output" - * }); - * } - * - * fn print_usage(program: &str, _opts: &[std::getopts::Opt]) { - * io::println(fmt!("Usage: %s [options]", program)); - * io::println("-o\t\tOutput"); - * io::println("-h --help\tUsage"); - * } - * - * fn main() { - * let args = os::args(); - * - * let program = copy args[0]; - * - * let opts = ~[ - * optopt("o"), - * optflag("h"), - * optflag("help") - * ]; - * let matches = match getopts(vec::tail(args), opts) { - * result::Ok(m) => { m } - * result::Err(f) => { fail!(fail_str(f)) } - * }; - * if opt_present(&matches, "h") || opt_present(&matches, "help") { - * print_usage(program, opts); - * return; - * } - * let output = opt_maybe_str(&matches, "o"); - * let input: &str = if !matches.free.is_empty() { - * copy matches.free[0] - * } else { - * print_usage(program, opts); - * return; - * }; - * do_work(input, output); - * } - * ``` - */ - -use core::cmp::Eq; -use core::result::{Err, Ok}; -use core::result; -use core::option::{Some, None}; -use core::str; -use core::vec; - -#[deriving(Eq)] -pub enum Name { - Long(~str), - Short(char), -} - -#[deriving(Eq)] -pub enum HasArg { Yes, No, Maybe, } - -#[deriving(Eq)] -pub enum Occur { Req, Optional, Multi, } - -/// A description of a possible option -#[deriving(Eq)] -pub struct Opt { - name: Name, - hasarg: HasArg, - occur: Occur -} - -fn mkname(nm: &str) -> Name { - let unm = str::to_owned(nm); - return if nm.len() == 1u { - Short(str::char_at(unm, 0u)) - } else { Long(unm) }; -} - -/// Create an option that is required and takes an argument -pub fn reqopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Req}; -} - -/// Create an option that is optional and takes an argument -pub fn optopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Optional}; -} - -/// Create an option that is optional and does not take an argument -pub fn optflag(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: No, occur: Optional}; -} - -/// Create an option that is optional and does not take an argument -pub fn optflagmulti(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: No, occur: Multi}; -} - -/// Create an option that is optional and takes an optional argument -pub fn optflagopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Maybe, occur: Optional}; -} - -/** - * Create an option that is optional, takes an argument, and may occur - * multiple times - */ -pub fn optmulti(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Multi}; -} - -#[deriving(Eq)] -enum Optval { Val(~str), Given, } - -/** - * The result of checking command line arguments. Contains a vector - * of matches and a vector of free strings. - */ -#[deriving(Eq)] -pub struct Matches { - opts: ~[Opt], - vals: ~[~[Optval]], - free: ~[~str] -} - -fn is_arg(arg: &str) -> bool { - return arg.len() > 1 && arg[0] == '-' as u8; -} - -fn name_str(nm: &Name) -> ~str { - return match *nm { - Short(ch) => str::from_char(ch), - Long(copy s) => s - }; -} - -fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> { - vec::position(opts, |opt| opt.name == nm) -} - -/** - * The type returned when the command line does not conform to the - * expected format. Pass this value to <fail_str> to get an error message. - */ -#[deriving(Eq)] -pub enum Fail_ { - ArgumentMissing(~str), - UnrecognizedOption(~str), - OptionMissing(~str), - OptionDuplicated(~str), - UnexpectedArgument(~str), -} - -/// Convert a `fail_` enum into an error string -pub fn fail_str(f: Fail_) -> ~str { - return match f { - ArgumentMissing(ref nm) => { - ~"Argument to option '" + *nm + ~"' missing." - } - UnrecognizedOption(ref nm) => { - ~"Unrecognized option: '" + *nm + ~"'." - } - OptionMissing(ref nm) => { - ~"Required option '" + *nm + ~"' missing." - } - OptionDuplicated(ref nm) => { - ~"Option '" + *nm + ~"' given more than once." - } - UnexpectedArgument(ref nm) => { - ~"Option " + *nm + ~" does not take an argument." - } - }; -} - -/** - * The result of parsing a command line with a set of options - * (result::t<Matches, Fail_>) - */ -pub type Result = result::Result<Matches, Fail_>; - -/** - * Parse command line arguments according to the provided options - * - * On success returns `ok(Opt)`. Use functions such as `opt_present` - * `opt_str`, etc. to interrogate results. Returns `err(Fail_)` on failure. - * Use <fail_str> to get an error message. - */ -pub fn getopts(args: &[~str], opts: &[Opt]) -> Result { - let n_opts = opts.len(); - fn f(_x: uint) -> ~[Optval] { return ~[]; } - let mut vals = vec::from_fn(n_opts, f); - let mut free: ~[~str] = ~[]; - let l = args.len(); - let mut i = 0; - while i < l { - let cur = copy args[i]; - let curlen = cur.len(); - if !is_arg(cur) { - free.push(cur); - } else if cur == ~"--" { - let mut j = i + 1; - while j < l { free.push(copy args[j]); j += 1; } - break; - } else { - let mut names; - let mut i_arg = None; - if cur[1] == '-' as u8 { - let tail = str::slice(cur, 2, curlen).to_owned(); - let mut tail_eq = ~[]; - for str::each_splitn_char(tail, '=', 1) |s| { tail_eq.push(s.to_owned()) } - if tail_eq.len() <= 1 { - names = ~[Long(tail)]; - } else { - names = - ~[Long(copy tail_eq[0])]; - i_arg = Some(copy tail_eq[1]); - } - } else { - let mut j = 1; - let mut last_valid_opt_id = None; - names = ~[]; - while j < curlen { - let range = str::char_range_at(cur, j); - let opt = Short(range.ch); - - /* In a series of potential options (eg. -aheJ), if we - see one which takes an argument, we assume all - subsequent characters make up the argument. This - allows options such as -L/usr/local/lib/foo to be - interpreted correctly - */ - - match find_opt(opts, copy opt) { - Some(id) => last_valid_opt_id = Some(id), - None => { - let arg_follows = - last_valid_opt_id.is_some() && - match opts[last_valid_opt_id.get()] - .hasarg { - - Yes | Maybe => true, - No => false - }; - if arg_follows && j < curlen { - i_arg = Some(cur.slice(j, curlen).to_owned()); - break; - } else { - last_valid_opt_id = None; - } - } - } - names.push(opt); - j = range.next; - } - } - let mut name_pos = 0; - for names.each() |nm| { - name_pos += 1; - let optid = match find_opt(opts, copy *nm) { - Some(id) => id, - None => return Err(UnrecognizedOption(name_str(nm))) - }; - match opts[optid].hasarg { - No => { - if !i_arg.is_none() { - return Err(UnexpectedArgument(name_str(nm))); - } - vals[optid].push(Given); - } - Maybe => { - if !i_arg.is_none() { - vals[optid].push(Val((copy i_arg).get())); - } else if name_pos < names.len() || - i + 1 == l || is_arg(args[i + 1]) { - vals[optid].push(Given); - } else { i += 1; vals[optid].push(Val(copy args[i])); } - } - Yes => { - if !i_arg.is_none() { - vals[optid].push(Val((copy i_arg).get())); - } else if i + 1 == l { - return Err(ArgumentMissing(name_str(nm))); - } else { i += 1; vals[optid].push(Val(copy args[i])); } - } - } - } - } - i += 1; - } - i = 0u; - while i < n_opts { - let n = vals[i].len(); - let occ = opts[i].occur; - if occ == Req { - if n == 0 { - return Err(OptionMissing(name_str(&(opts[i].name)))); - } - } - if occ != Multi { - if n > 1 { - return Err(OptionDuplicated(name_str(&(opts[i].name)))); - } - } - i += 1; - } - return Ok(Matches {opts: vec::to_owned(opts), - vals: vals, - free: free}); -} - -fn opt_vals(mm: &Matches, nm: &str) -> ~[Optval] { - return match find_opt(mm.opts, mkname(nm)) { - Some(id) => copy mm.vals[id], - None => { - error!("No option '%s' defined", nm); - fail!() - } - }; -} - -fn opt_val(mm: &Matches, nm: &str) -> Optval { copy opt_vals(mm, nm)[0] } - -/// Returns true if an option was matched -pub fn opt_present(mm: &Matches, nm: &str) -> bool { - !opt_vals(mm, nm).is_empty() -} - -/// Returns the number of times an option was matched -pub fn opt_count(mm: &Matches, nm: &str) -> uint { - opt_vals(mm, nm).len() -} - -/// Returns true if any of several options were matched -pub fn opts_present(mm: &Matches, names: &[~str]) -> bool { - for names.each |nm| { - match find_opt(mm.opts, mkname(*nm)) { - Some(id) if !mm.vals[id].is_empty() => return true, - _ => (), - }; - } - false -} - - -/** - * Returns the string argument supplied to a matching option - * - * Fails if the option was not matched or if the match did not take an - * argument - */ -pub fn opt_str(mm: &Matches, nm: &str) -> ~str { - return match opt_val(mm, nm) { Val(copy s) => s, _ => fail!() }; -} - -/** - * Returns the string argument supplied to one of several matching options - * - * Fails if the no option was provided from the given list, or if the no such - * option took an argument - */ -pub fn opts_str(mm: &Matches, names: &[~str]) -> ~str { - for names.each |nm| { - match opt_val(mm, *nm) { - Val(copy s) => return s, - _ => () - } - } - fail!(); -} - - -/** - * Returns a vector of the arguments provided to all matches of the given - * option. - * - * Used when an option accepts multiple values. - */ -pub fn opt_strs(mm: &Matches, nm: &str) -> ~[~str] { - let mut acc: ~[~str] = ~[]; - for vec::each(opt_vals(mm, nm)) |v| { - match *v { Val(copy s) => acc.push(s), _ => () } - } - return acc; -} - -/// Returns the string argument supplied to a matching option or none -pub fn opt_maybe_str(mm: &Matches, nm: &str) -> Option<~str> { - let vals = opt_vals(mm, nm); - if vec::len::<Optval>(vals) == 0u { return None::<~str>; } - return match vals[0] { - Val(copy s) => Some(s), - _ => None - }; -} - - -/** - * Returns the matching string, a default, or none - * - * Returns none if the option was not present, `def` if the option was - * present but no argument was provided, and the argument if the option was - * present and an argument was provided. - */ -pub fn opt_default(mm: &Matches, nm: &str, def: &str) -> Option<~str> { - let vals = opt_vals(mm, nm); - if vec::len::<Optval>(vals) == 0u { return None::<~str>; } - return match vals[0] { Val(copy s) => Some::<~str>(s), - _ => Some::<~str>(str::to_owned(def)) } -} - -#[deriving(Eq)] -pub enum FailType { - ArgumentMissing_, - UnrecognizedOption_, - OptionMissing_, - OptionDuplicated_, - UnexpectedArgument_, -} - -/** A module which provides a way to specify descriptions and - * groups of short and long option names, together. - */ -pub mod groups { - use getopts::{HasArg, Long, Maybe, Multi, No, Occur, Opt, Optional, Req}; - use getopts::{Short, Yes}; - - use core::str; - use core::vec; - - /** one group of options, e.g., both -h and --help, along with - * their shared description and properties - */ - #[deriving(Eq)] - pub struct OptGroup { - short_name: ~str, - long_name: ~str, - hint: ~str, - desc: ~str, - hasarg: HasArg, - occur: Occur - } - - /// Create a long option that is required and takes an argument - pub fn reqopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { - let len = short_name.len(); - assert!(len == 1 || len == 0); - return OptGroup { short_name: str::to_owned(short_name), - long_name: str::to_owned(long_name), - hint: str::to_owned(hint), - desc: str::to_owned(desc), - hasarg: Yes, - occur: Req}; - } - - /// Create a long option that is optional and takes an argument - pub fn optopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { - let len = short_name.len(); - assert!(len == 1 || len == 0); - return OptGroup {short_name: str::to_owned(short_name), - long_name: str::to_owned(long_name), - hint: str::to_owned(hint), - desc: str::to_owned(desc), - hasarg: Yes, - occur: Optional}; - } - - /// Create a long option that is optional and does not take an argument - pub fn optflag(short_name: &str, long_name: &str, - desc: &str) -> OptGroup { - let len = short_name.len(); - assert!(len == 1 || len == 0); - return OptGroup {short_name: str::to_owned(short_name), - long_name: str::to_owned(long_name), - hint: ~"", - desc: str::to_owned(desc), - hasarg: No, - occur: Optional}; - } - - /// Create a long option that is optional and takes an optional argument - pub fn optflagopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { - let len = short_name.len(); - assert!(len == 1 || len == 0); - return OptGroup {short_name: str::to_owned(short_name), - long_name: str::to_owned(long_name), - hint: str::to_owned(hint), - desc: str::to_owned(desc), - hasarg: Maybe, - occur: Optional}; - } - - /** - * Create a long option that is optional, takes an argument, and may occur - * multiple times - */ - pub fn optmulti(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { - let len = short_name.len(); - assert!(len == 1 || len == 0); - return OptGroup {short_name: str::to_owned(short_name), - long_name: str::to_owned(long_name), - hint: str::to_owned(hint), - desc: str::to_owned(desc), - hasarg: Yes, - occur: Multi}; - } - - // translate OptGroup into Opt - // (both short and long names correspond to different Opts) - pub fn long_to_short(lopt: &OptGroup) -> ~[Opt] { - let OptGroup{short_name: short_name, - long_name: long_name, - hasarg: hasarg, - occur: occur, - _} = copy *lopt; - - match (short_name.len(), long_name.len()) { - (0,0) => fail!("this long-format option was given no name"), - - (0,_) => ~[Opt {name: Long((long_name)), - hasarg: hasarg, - occur: occur}], - - (1,0) => ~[Opt {name: Short(str::char_at(short_name, 0)), - hasarg: hasarg, - occur: occur}], - - (1,_) => ~[Opt {name: Short(str::char_at(short_name, 0)), - hasarg: hasarg, - occur: occur}, - Opt {name: Long((long_name)), - hasarg: hasarg, - occur: occur}], - - (_,_) => fail!("something is wrong with the long-form opt") - } - } - - /* - * Parse command line args with the provided long format options - */ - pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result { - ::getopts::getopts(args, vec::flat_map(opts, long_to_short)) - } - - /** - * Derive a usage message from a set of long options - */ - pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str { - - let desc_sep = ~"\n" + str::repeat(" ", 24); - - let rows = vec::map(opts, |optref| { - let OptGroup{short_name: short_name, - long_name: long_name, - hint: hint, - desc: desc, - hasarg: hasarg, - _} = copy *optref; - - let mut row = str::repeat(" ", 4); - - // short option - row += match short_name.len() { - 0 => ~"", - 1 => ~"-" + short_name + " ", - _ => fail!("the short name should only be 1 ascii char long"), - }; - - // long option - row += match long_name.len() { - 0 => ~"", - _ => ~"--" + long_name + " ", - }; - - // arg - row += match hasarg { - No => ~"", - Yes => hint, - Maybe => ~"[" + hint + ~"]", - }; - - // FIXME: #5516 - // here we just need to indent the start of the description - let rowlen = row.len(); - row += if rowlen < 24 { - str::repeat(" ", 24 - rowlen) - } else { - copy desc_sep - }; - - // Normalize desc to contain words separated by one space character - let mut desc_normalized_whitespace = ~""; - for str::each_word(desc) |word| { - desc_normalized_whitespace.push_str(word); - desc_normalized_whitespace.push_char(' '); - } - - // FIXME: #5516 - let mut desc_rows = ~[]; - for str::each_split_within(desc_normalized_whitespace, 54) |substr| { - desc_rows.push(substr.to_owned()); - } - - // FIXME: #5516 - // wrapped description - row += str::connect(desc_rows, desc_sep); - - row - }); - - return str::to_owned(brief) + - ~"\n\nOptions:\n" + - str::connect(rows, "\n") + - ~"\n\n"; - } -} // end groups module - -#[cfg(test)] -mod tests { - - use getopts::groups::OptGroup; - use getopts::*; - - use core::result::{Err, Ok}; - use core::result; - - fn check_fail_type(f: Fail_, ft: FailType) { - match f { - ArgumentMissing(_) => assert!(ft == ArgumentMissing_), - UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_), - OptionMissing(_) => assert!(ft == OptionMissing_), - OptionDuplicated(_) => assert!(ft == OptionDuplicated_), - UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_) - } - } - - - // Tests for reqopt - #[test] - fn test_reqopt_long() { - let args = ~[~"--test=20"]; - let opts = ~[reqopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"test"))); - assert_eq!(opt_str(m, ~"test"), ~"20"); - } - _ => { fail!("test_reqopt_long failed"); } - } - } - - #[test] - fn test_reqopt_long_missing() { - let args = ~[~"blah"]; - let opts = ~[reqopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionMissing_), - _ => fail!() - } - } - - #[test] - fn test_reqopt_long_no_arg() { - let args = ~[~"--test"]; - let opts = ~[reqopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_reqopt_long_multi() { - let args = ~[~"--test=20", ~"--test=30"]; - let opts = ~[reqopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - #[test] - fn test_reqopt_short() { - let args = ~[~"-t", ~"20"]; - let opts = ~[reqopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"t"))); - assert_eq!(opt_str(m, ~"t"), ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_reqopt_short_missing() { - let args = ~[~"blah"]; - let opts = ~[reqopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionMissing_), - _ => fail!() - } - } - - #[test] - fn test_reqopt_short_no_arg() { - let args = ~[~"-t"]; - let opts = ~[reqopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_reqopt_short_multi() { - let args = ~[~"-t", ~"20", ~"-t", ~"30"]; - let opts = ~[reqopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - - // Tests for optopt - #[test] - fn test_optopt_long() { - let args = ~[~"--test=20"]; - let opts = ~[optopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"test"))); - assert_eq!(opt_str(m, ~"test"), ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_optopt_long_missing() { - let args = ~[~"blah"]; - let opts = ~[optopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"test")), - _ => fail!() - } - } - - #[test] - fn test_optopt_long_no_arg() { - let args = ~[~"--test"]; - let opts = ~[optopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_optopt_long_multi() { - let args = ~[~"--test=20", ~"--test=30"]; - let opts = ~[optopt(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - #[test] - fn test_optopt_short() { - let args = ~[~"-t", ~"20"]; - let opts = ~[optopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"t"))); - assert_eq!(opt_str(m, ~"t"), ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_optopt_short_missing() { - let args = ~[~"blah"]; - let opts = ~[optopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"t")), - _ => fail!() - } - } - - #[test] - fn test_optopt_short_no_arg() { - let args = ~[~"-t"]; - let opts = ~[optopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_optopt_short_multi() { - let args = ~[~"-t", ~"20", ~"-t", ~"30"]; - let opts = ~[optopt(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - - // Tests for optflag - #[test] - fn test_optflag_long() { - let args = ~[~"--test"]; - let opts = ~[optflag(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(opt_present(m, ~"test")), - _ => fail!() - } - } - - #[test] - fn test_optflag_long_missing() { - let args = ~[~"blah"]; - let opts = ~[optflag(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"test")), - _ => fail!() - } - } - - #[test] - fn test_optflag_long_arg() { - let args = ~[~"--test=20"]; - let opts = ~[optflag(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => { - error!(fail_str(copy f)); - check_fail_type(f, UnexpectedArgument_); - } - _ => fail!() - } - } - - #[test] - fn test_optflag_long_multi() { - let args = ~[~"--test", ~"--test"]; - let opts = ~[optflag(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - #[test] - fn test_optflag_short() { - let args = ~[~"-t"]; - let opts = ~[optflag(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(opt_present(m, ~"t")), - _ => fail!() - } - } - - #[test] - fn test_optflag_short_missing() { - let args = ~[~"blah"]; - let opts = ~[optflag(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"t")), - _ => fail!() - } - } - - #[test] - fn test_optflag_short_arg() { - let args = ~[~"-t", ~"20"]; - let opts = ~[optflag(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - // The next variable after the flag is just a free argument - - assert!(m.free[0] == ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_optflag_short_multi() { - let args = ~[~"-t", ~"-t"]; - let opts = ~[optflag(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, OptionDuplicated_), - _ => fail!() - } - } - - // Tests for optflagmulti - #[test] - fn test_optflagmulti_short1() { - let args = ~[~"-v"]; - let opts = ~[optflagmulti(~"v")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert_eq!(opt_count(m, ~"v"), 1); - } - _ => fail!() - } - } - - #[test] - fn test_optflagmulti_short2a() { - let args = ~[~"-v", ~"-v"]; - let opts = ~[optflagmulti(~"v")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert_eq!(opt_count(m, ~"v"), 2); - } - _ => fail!() - } - } - - #[test] - fn test_optflagmulti_short2b() { - let args = ~[~"-vv"]; - let opts = ~[optflagmulti(~"v")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert_eq!(opt_count(m, ~"v"), 2); - } - _ => fail!() - } - } - - #[test] - fn test_optflagmulti_long1() { - let args = ~[~"--verbose"]; - let opts = ~[optflagmulti(~"verbose")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert_eq!(opt_count(m, ~"verbose"), 1); - } - _ => fail!() - } - } - - #[test] - fn test_optflagmulti_long2() { - let args = ~[~"--verbose", ~"--verbose"]; - let opts = ~[optflagmulti(~"verbose")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert_eq!(opt_count(m, ~"verbose"), 2); - } - _ => fail!() - } - } - - // Tests for optmulti - #[test] - fn test_optmulti_long() { - let args = ~[~"--test=20"]; - let opts = ~[optmulti(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"test"))); - assert_eq!(opt_str(m, ~"test"), ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_optmulti_long_missing() { - let args = ~[~"blah"]; - let opts = ~[optmulti(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"test")), - _ => fail!() - } - } - - #[test] - fn test_optmulti_long_no_arg() { - let args = ~[~"--test"]; - let opts = ~[optmulti(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_optmulti_long_multi() { - let args = ~[~"--test=20", ~"--test=30"]; - let opts = ~[optmulti(~"test")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!(opt_present(m, ~"test")); - assert_eq!(opt_str(m, ~"test"), ~"20"); - let pair = opt_strs(m, ~"test"); - assert!(pair[0] == ~"20"); - assert!(pair[1] == ~"30"); - } - _ => fail!() - } - } - - #[test] - fn test_optmulti_short() { - let args = ~[~"-t", ~"20"]; - let opts = ~[optmulti(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"t"))); - assert_eq!(opt_str(m, ~"t"), ~"20"); - } - _ => fail!() - } - } - - #[test] - fn test_optmulti_short_missing() { - let args = ~[~"blah"]; - let opts = ~[optmulti(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => assert!(!opt_present(m, ~"t")), - _ => fail!() - } - } - - #[test] - fn test_optmulti_short_no_arg() { - let args = ~[~"-t"]; - let opts = ~[optmulti(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, ArgumentMissing_), - _ => fail!() - } - } - - #[test] - fn test_optmulti_short_multi() { - let args = ~[~"-t", ~"20", ~"-t", ~"30"]; - let opts = ~[optmulti(~"t")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!((opt_present(m, ~"t"))); - assert_eq!(opt_str(m, ~"t"), ~"20"); - let pair = opt_strs(m, ~"t"); - assert!(pair[0] == ~"20"); - assert!(pair[1] == ~"30"); - } - _ => fail!() - } - } - - #[test] - fn test_unrecognized_option_long() { - let args = ~[~"--untest"]; - let opts = ~[optmulti(~"t")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, UnrecognizedOption_), - _ => fail!() - } - } - - #[test] - fn test_unrecognized_option_short() { - let args = ~[~"-t"]; - let opts = ~[optmulti(~"test")]; - let rs = getopts(args, opts); - match rs { - Err(copy f) => check_fail_type(f, UnrecognizedOption_), - _ => fail!() - } - } - - #[test] - fn test_combined() { - let args = - ~[~"prog", ~"free1", ~"-s", ~"20", ~"free2", - ~"--flag", ~"--long=30", ~"-f", ~"-m", ~"40", - ~"-m", ~"50", ~"-n", ~"-A B", ~"-n", ~"-60 70"]; - let opts = - ~[optopt(~"s"), optflag(~"flag"), reqopt(~"long"), - optflag(~"f"), optmulti(~"m"), optmulti(~"n"), - optopt(~"notpresent")]; - let rs = getopts(args, opts); - match rs { - Ok(ref m) => { - assert!(m.free[0] == ~"prog"); - assert!(m.free[1] == ~"free1"); - assert_eq!(opt_str(m, ~"s"), ~"20"); - assert!(m.free[2] == ~"free2"); - assert!((opt_present(m, ~"flag"))); - assert_eq!(opt_str(m, ~"long"), ~"30"); - assert!((opt_present(m, ~"f"))); - let pair = opt_strs(m, ~"m"); - assert!(pair[0] == ~"40"); - assert!(pair[1] == ~"50"); - let pair = opt_strs(m, ~"n"); - assert!(pair[0] == ~"-A B"); - assert!(pair[1] == ~"-60 70"); - assert!((!opt_present(m, ~"notpresent"))); - } - _ => fail!() - } - } - - #[test] - fn test_multi() { - let args = ~[~"-e", ~"foo", ~"--encrypt", ~"foo"]; - let opts = ~[optopt(~"e"), optopt(~"encrypt"), optopt(~"f")]; - let matches = &match getopts(args, opts) { - result::Ok(m) => m, - result::Err(_) => fail!() - }; - assert!(opts_present(matches, ~[~"e"])); - assert!(opts_present(matches, ~[~"encrypt"])); - assert!(opts_present(matches, ~[~"encrypt", ~"e"])); - assert!(opts_present(matches, ~[~"e", ~"encrypt"])); - assert!(!opts_present(matches, ~[~"f"])); - assert!(!opts_present(matches, ~[~"thing"])); - assert!(!opts_present(matches, ~[])); - - assert_eq!(opts_str(matches, ~[~"e"]), ~"foo"); - assert_eq!(opts_str(matches, ~[~"encrypt"]), ~"foo"); - assert_eq!(opts_str(matches, ~[~"e", ~"encrypt"]), ~"foo"); - assert_eq!(opts_str(matches, ~[~"encrypt", ~"e"]), ~"foo"); - } - - #[test] - fn test_nospace() { - let args = ~[~"-Lfoo", ~"-M."]; - let opts = ~[optmulti(~"L"), optmulti(~"M")]; - let matches = &match getopts(args, opts) { - result::Ok(m) => m, - result::Err(_) => fail!() - }; - assert!(opts_present(matches, ~[~"L"])); - assert_eq!(opts_str(matches, ~[~"L"]), ~"foo"); - assert!(opts_present(matches, ~[~"M"])); - assert_eq!(opts_str(matches, ~[~"M"]), ~"."); - - } - - #[test] - fn test_groups_reqopt() { - let opt = groups::reqopt(~"b", ~"banana", ~"some bananas", ~"VAL"); - assert!(opt == OptGroup { short_name: ~"b", - long_name: ~"banana", - hint: ~"VAL", - desc: ~"some bananas", - hasarg: Yes, - occur: Req }) - } - - #[test] - fn test_groups_optopt() { - let opt = groups::optopt(~"a", ~"apple", ~"some apples", ~"VAL"); - assert!(opt == OptGroup { short_name: ~"a", - long_name: ~"apple", - hint: ~"VAL", - desc: ~"some apples", - hasarg: Yes, - occur: Optional }) - } - - #[test] - fn test_groups_optflag() { - let opt = groups::optflag(~"k", ~"kiwi", ~"some kiwis"); - assert!(opt == OptGroup { short_name: ~"k", - long_name: ~"kiwi", - hint: ~"", - desc: ~"some kiwis", - hasarg: No, - occur: Optional }) - } - - #[test] - fn test_groups_optflagopt() { - let opt = groups::optflagopt(~"p", ~"pineapple", - ~"some pineapples", ~"VAL"); - assert!(opt == OptGroup { short_name: ~"p", - long_name: ~"pineapple", - hint: ~"VAL", - desc: ~"some pineapples", - hasarg: Maybe, - occur: Optional }) - } - - #[test] - fn test_groups_optmulti() { - let opt = groups::optmulti(~"l", ~"lime", - ~"some limes", ~"VAL"); - assert!(opt == OptGroup { short_name: ~"l", - long_name: ~"lime", - hint: ~"VAL", - desc: ~"some limes", - hasarg: Yes, - occur: Multi }) - } - - #[test] - fn test_groups_long_to_short() { - let short = ~[reqopt(~"b"), reqopt(~"banana")]; - let verbose = groups::reqopt(~"b", ~"banana", - ~"some bananas", ~"VAL"); - - assert_eq!(groups::long_to_short(&verbose), short); - } - - #[test] - fn test_groups_getopts() { - let short = ~[ - reqopt(~"b"), reqopt(~"banana"), - optopt(~"a"), optopt(~"apple"), - optflag(~"k"), optflagopt(~"kiwi"), - optflagopt(~"p"), - optmulti(~"l") - ]; - - let verbose = ~[ - groups::reqopt(~"b", ~"banana", ~"Desc", ~"VAL"), - groups::optopt(~"a", ~"apple", ~"Desc", ~"VAL"), - groups::optflag(~"k", ~"kiwi", ~"Desc"), - groups::optflagopt(~"p", ~"", ~"Desc", ~"VAL"), - groups::optmulti(~"l", ~"", ~"Desc", ~"VAL"), - ]; - - let sample_args = ~[~"-k", ~"15", ~"--apple", ~"1", ~"k", - ~"-p", ~"16", ~"l", ~"35"]; - - // FIXME #4681: sort options here? - assert!(getopts(sample_args, short) - == groups::getopts(sample_args, verbose)); - } - - #[test] - fn test_groups_usage() { - let optgroups = ~[ - groups::reqopt(~"b", ~"banana", ~"Desc", ~"VAL"), - groups::optopt(~"a", ~"012345678901234567890123456789", - ~"Desc", ~"VAL"), - groups::optflag(~"k", ~"kiwi", ~"Desc"), - groups::optflagopt(~"p", ~"", ~"Desc", ~"VAL"), - groups::optmulti(~"l", ~"", ~"Desc", ~"VAL"), - ]; - - let expected = -~"Usage: fruits - -Options: - -b --banana VAL Desc - -a --012345678901234567890123456789 VAL - Desc - -k --kiwi Desc - -p [VAL] Desc - -l VAL Desc - -"; - - let generated_usage = groups::usage(~"Usage: fruits", optgroups); - - debug!("expected: <<%s>>", expected); - debug!("generated: <<%s>>", generated_usage); - assert_eq!(generated_usage, expected); - } - - #[test] - fn test_groups_usage_description_wrapping() { - // indentation should be 24 spaces - // lines wrap after 78: or rather descriptions wrap after 54 - - let optgroups = ~[ - groups::optflag(~"k", ~"kiwi", - ~"This is a long description which won't be wrapped..+.."), // 54 - groups::optflag(~"a", ~"apple", - ~"This is a long description which _will_ be wrapped..+.."), // 55 - ]; - - let expected = -~"Usage: fruits - -Options: - -k --kiwi This is a long description which won't be wrapped..+.. - -a --apple This is a long description which _will_ be - wrapped..+.. - -"; - - let usage = groups::usage(~"Usage: fruits", optgroups); - - debug!("expected: <<%s>>", expected); - debug!("generated: <<%s>>", usage); - assert!(usage == expected) - } -} diff --git a/src/libstd/hash.rs b/src/libstd/hash.rs new file mode 100644 index 00000000000..69312f3a97b --- /dev/null +++ b/src/libstd/hash.rs @@ -0,0 +1,557 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + * Implementation of SipHash 2-4 + * + * See: http://131002.net/siphash/ + * + * Consider this as a main "general-purpose" hash for all hashtables: it + * runs at good speed (competitive with spooky and city) and permits + * cryptographically strong _keyed_ hashing. Key your hashtables from a + * CPRNG like rand::rng. + */ + +use container::Container; +use old_iter::BaseIter; +use rt::io::Writer; +use to_bytes::IterBytes; +use uint; + +// Alias `SipState` to `State`. +pub use State = hash::SipState; + +/** + * Types that can meaningfully be hashed should implement this. + * + * Note that this trait is likely to change somewhat as it is + * closely related to `to_bytes::IterBytes` and in almost all + * cases presently the two are (and must be) used together. + * + * In general, most types only need to implement `IterBytes`, + * and the implementation of `Hash` below will take care of + * the rest. This is the recommended approach, since constructing + * good keyed hash functions is quite difficult. + */ +pub trait Hash { + /** + * Compute a "keyed" hash of the value implementing the trait, + * taking `k0` and `k1` as "keying" parameters that randomize or + * otherwise perturb the hash function in such a way that a + * hash table built using such "keyed hash functions" cannot + * be made to perform linearly by an attacker controlling the + * hashtable's contents. + * + * In practical terms, we implement this using the SipHash 2-4 + * function and require most types to only implement the + * IterBytes trait, that feeds SipHash. + */ + fn hash_keyed(&self, k0: u64, k1: u64) -> u64; +} + +// When we have default methods, won't need this. +pub trait HashUtil { + fn hash(&self) -> u64; +} + +impl<A:Hash> HashUtil for A { + #[inline(always)] + fn hash(&self) -> u64 { self.hash_keyed(0,0) } +} + +/// Streaming hash-functions should implement this. +pub trait Streaming { + fn input(&mut self, &[u8]); + // These can be refactored some when we have default methods. + fn result_bytes(&mut self) -> ~[u8]; + fn result_str(&mut self) -> ~str; + fn result_u64(&mut self) -> u64; + fn reset(&mut self); +} + +impl<A:IterBytes> Hash for A { + #[inline(always)] + fn hash_keyed(&self, k0: u64, k1: u64) -> u64 { + let mut s = State::new(k0, k1); + for self.iter_bytes(true) |bytes| { + s.input(bytes); + } + s.result_u64() + } +} + +fn hash_keyed_2<A: IterBytes, + B: IterBytes>(a: &A, b: &B, k0: u64, k1: u64) -> u64 { + let mut s = State::new(k0, k1); + for a.iter_bytes(true) |bytes| { + s.input(bytes); + } + for b.iter_bytes(true) |bytes| { + s.input(bytes); + } + s.result_u64() +} + +fn hash_keyed_3<A: IterBytes, + B: IterBytes, + C: IterBytes>(a: &A, b: &B, c: &C, k0: u64, k1: u64) -> u64 { + let mut s = State::new(k0, k1); + for a.iter_bytes(true) |bytes| { + s.input(bytes); + } + for b.iter_bytes(true) |bytes| { + s.input(bytes); + } + for c.iter_bytes(true) |bytes| { + s.input(bytes); + } + s.result_u64() +} + +fn hash_keyed_4<A: IterBytes, + B: IterBytes, + C: IterBytes, + D: IterBytes>( + a: &A, + b: &B, + c: &C, + d: &D, + k0: u64, + k1: u64) + -> u64 { + let mut s = State::new(k0, k1); + for a.iter_bytes(true) |bytes| { + s.input(bytes); + } + for b.iter_bytes(true) |bytes| { + s.input(bytes); + } + for c.iter_bytes(true) |bytes| { + s.input(bytes); + } + for d.iter_bytes(true) |bytes| { + s.input(bytes); + } + s.result_u64() +} + +fn hash_keyed_5<A: IterBytes, + B: IterBytes, + C: IterBytes, + D: IterBytes, + E: IterBytes>( + a: &A, + b: &B, + c: &C, + d: &D, + e: &E, + k0: u64, + k1: u64) + -> u64 { + let mut s = State::new(k0, k1); + for a.iter_bytes(true) |bytes| { + s.input(bytes); + } + for b.iter_bytes(true) |bytes| { + s.input(bytes); + } + for c.iter_bytes(true) |bytes| { + s.input(bytes); + } + for d.iter_bytes(true) |bytes| { + s.input(bytes); + } + for e.iter_bytes(true) |bytes| { + s.input(bytes); + } + s.result_u64() +} + +#[inline(always)] +pub fn default_state() -> State { + State::new(0, 0) +} + +struct SipState { + k0: u64, + k1: u64, + length: uint, // how many bytes we've processed + v0: u64, // hash state + v1: u64, + v2: u64, + v3: u64, + tail: [u8, ..8], // unprocessed bytes + ntail: uint, // how many bytes in tail are valid +} + +impl SipState { + #[inline(always)] + fn new(key0: u64, key1: u64) -> SipState { + let mut state = SipState { + k0: key0, + k1: key1, + length: 0, + v0: 0, + v1: 0, + v2: 0, + v3: 0, + tail: [ 0, 0, 0, 0, 0, 0, 0, 0 ], + ntail: 0, + }; + state.reset(); + state + } +} + +// sadly, these macro definitions can't appear later, +// because they're needed in the following defs; +// this design could be improved. + +macro_rules! u8to64_le ( + ($buf:expr, $i:expr) => + ($buf[0+$i] as u64 | + $buf[1+$i] as u64 << 8 | + $buf[2+$i] as u64 << 16 | + $buf[3+$i] as u64 << 24 | + $buf[4+$i] as u64 << 32 | + $buf[5+$i] as u64 << 40 | + $buf[6+$i] as u64 << 48 | + $buf[7+$i] as u64 << 56) +) + +macro_rules! rotl ( + ($x:expr, $b:expr) => + (($x << $b) | ($x >> (64 - $b))) +) + +macro_rules! compress ( + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => + ({ + $v0 += $v1; $v1 = rotl!($v1, 13); $v1 ^= $v0; + $v0 = rotl!($v0, 32); + $v2 += $v3; $v3 = rotl!($v3, 16); $v3 ^= $v2; + $v0 += $v3; $v3 = rotl!($v3, 21); $v3 ^= $v0; + $v2 += $v1; $v1 = rotl!($v1, 17); $v1 ^= $v2; + $v2 = rotl!($v2, 32); + }) +) + + +impl Writer for SipState { + // Methods for io::writer + #[inline(always)] + fn write(&mut self, msg: &[u8]) { + let length = msg.len(); + self.length += length; + + let mut needed = 0u; + + if self.ntail != 0 { + needed = 8 - self.ntail; + + if length < needed { + let mut t = 0; + while t < length { + self.tail[self.ntail+t] = msg[t]; + t += 1; + } + self.ntail += length; + return; + } + + let mut t = 0; + while t < needed { + self.tail[self.ntail+t] = msg[t]; + t += 1; + } + + let m = u8to64_le!(self.tail, 0); + + self.v3 ^= m; + compress!(self.v0, self.v1, self.v2, self.v3); + compress!(self.v0, self.v1, self.v2, self.v3); + self.v0 ^= m; + + self.ntail = 0; + } + + // Buffered tail is now flushed, process new input. + let len = length - needed; + let end = len & (!0x7); + let left = len & 0x7; + + let mut i = needed; + while i < end { + let mi = u8to64_le!(msg, i); + + self.v3 ^= mi; + compress!(self.v0, self.v1, self.v2, self.v3); + compress!(self.v0, self.v1, self.v2, self.v3); + self.v0 ^= mi; + + i += 8; + } + + let mut t = 0u; + while t < left { + self.tail[t] = msg[i+t]; + t += 1 + } + self.ntail = left; + } + + fn flush(&mut self) { + // No-op + } +} + +impl Streaming for SipState { + #[inline(always)] + fn input(&mut self, buf: &[u8]) { + self.write(buf); + } + + #[inline(always)] + fn result_u64(&mut self) -> u64 { + let mut v0 = self.v0; + let mut v1 = self.v1; + let mut v2 = self.v2; + let mut v3 = self.v3; + + let mut b : u64 = (self.length as u64 & 0xff) << 56; + + if self.ntail > 0 { b |= self.tail[0] as u64 << 0; } + if self.ntail > 1 { b |= self.tail[1] as u64 << 8; } + if self.ntail > 2 { b |= self.tail[2] as u64 << 16; } + if self.ntail > 3 { b |= self.tail[3] as u64 << 24; } + if self.ntail > 4 { b |= self.tail[4] as u64 << 32; } + if self.ntail > 5 { b |= self.tail[5] as u64 << 40; } + if self.ntail > 6 { b |= self.tail[6] as u64 << 48; } + + v3 ^= b; + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + v0 ^= b; + + v2 ^= 0xff; + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + compress!(v0, v1, v2, v3); + + return (v0 ^ v1 ^ v2 ^ v3); + } + + fn result_bytes(&mut self) -> ~[u8] { + let h = self.result_u64(); + ~[(h >> 0) as u8, + (h >> 8) as u8, + (h >> 16) as u8, + (h >> 24) as u8, + (h >> 32) as u8, + (h >> 40) as u8, + (h >> 48) as u8, + (h >> 56) as u8, + ] + } + + fn result_str(&mut self) -> ~str { + let r = self.result_bytes(); + let mut s = ~""; + for r.each |b| { + s += uint::to_str_radix(*b as uint, 16u); + } + s + } + + #[inline(always)] + fn reset(&mut self) { + self.length = 0; + self.v0 = self.k0 ^ 0x736f6d6570736575; + self.v1 = self.k1 ^ 0x646f72616e646f6d; + self.v2 = self.k0 ^ 0x6c7967656e657261; + self.v3 = self.k1 ^ 0x7465646279746573; + self.ntail = 0; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use prelude::*; + + #[test] + fn test_siphash() { + let vecs : [[u8, ..8], ..64] = [ + [ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, ], + [ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, ], + [ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, ], + [ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, ], + [ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, ], + [ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, ], + [ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, ], + [ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, ], + [ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, ], + [ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, ], + [ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, ], + [ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, ], + [ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, ], + [ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, ], + [ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, ], + [ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, ], + [ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, ], + [ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, ], + [ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, ], + [ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, ], + [ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, ], + [ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, ], + [ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, ], + [ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, ], + [ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, ], + [ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, ], + [ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, ], + [ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, ], + [ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, ], + [ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, ], + [ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, ], + [ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, ], + [ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, ], + [ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, ], + [ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, ], + [ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, ], + [ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, ], + [ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, ], + [ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, ], + [ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, ], + [ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, ], + [ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, ], + [ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, ], + [ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, ], + [ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, ], + [ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, ], + [ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, ], + [ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, ], + [ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, ], + [ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, ], + [ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, ], + [ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, ], + [ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, ], + [ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, ], + [ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, ], + [ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, ], + [ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, ], + [ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, ], + [ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, ], + [ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, ], + [ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, ], + [ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, ], + [ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, ], + [ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, ] + ]; + + let k0 = 0x_07_06_05_04_03_02_01_00_u64; + let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08_u64; + let mut buf : ~[u8] = ~[]; + let mut t = 0; + let mut stream_inc = SipState::new(k0, k1); + let mut stream_full = SipState::new(k0, k1); + + fn to_hex_str(r: &[u8, ..8]) -> ~str { + let mut s = ~""; + for (*r).each |b| { + s += uint::to_str_radix(*b as uint, 16u); + } + s + } + + while t < 64 { + debug!("siphash test %?", t); + let vec = u8to64_le!(vecs[t], 0); + let out = buf.hash_keyed(k0, k1); + debug!("got %?, expected %?", out, vec); + assert_eq!(vec, out); + + stream_full.reset(); + stream_full.input(buf); + let f = stream_full.result_str(); + let i = stream_inc.result_str(); + let v = to_hex_str(&vecs[t]); + debug!("%d: (%s) => inc=%s full=%s", t, v, i, f); + + assert!(f == i && f == v); + + buf += ~[t as u8]; + stream_inc.input(~[t as u8]); + + t += 1; + } + } + + #[test] #[cfg(target_arch = "arm")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert!((val as u64).hash() != (val as uint).hash()); + assert_eq!((val as u32).hash(), (val as uint).hash()); + } + #[test] #[cfg(target_arch = "x86_64")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert_eq!((val as u64).hash(), (val as uint).hash()); + assert!((val as u32).hash() != (val as uint).hash()); + } + #[test] #[cfg(target_arch = "x86")] + fn test_hash_uint() { + let val = 0xdeadbeef_deadbeef_u64; + assert!((val as u64).hash() != (val as uint).hash()); + assert_eq!((val as u32).hash(), (val as uint).hash()); + } + + #[test] + fn test_hash_idempotent() { + let val64 = 0xdeadbeef_deadbeef_u64; + val64.hash() == val64.hash(); + let val32 = 0xdeadbeef_u32; + val32.hash() == val32.hash(); + } + + #[test] + fn test_hash_no_bytes_dropped_64() { + let val = 0xdeadbeef_deadbeef_u64; + + assert!(val.hash() != zero_byte(val, 0).hash()); + assert!(val.hash() != zero_byte(val, 1).hash()); + assert!(val.hash() != zero_byte(val, 2).hash()); + assert!(val.hash() != zero_byte(val, 3).hash()); + assert!(val.hash() != zero_byte(val, 4).hash()); + assert!(val.hash() != zero_byte(val, 5).hash()); + assert!(val.hash() != zero_byte(val, 6).hash()); + assert!(val.hash() != zero_byte(val, 7).hash()); + + fn zero_byte(val: u64, byte: uint) -> u64 { + assert!(byte < 8); + val & !(0xff << (byte * 8)) + } + } + + #[test] + fn test_hash_no_bytes_dropped_32() { + let val = 0xdeadbeef_u32; + + assert!(val.hash() != zero_byte(val, 0).hash()); + assert!(val.hash() != zero_byte(val, 1).hash()); + assert!(val.hash() != zero_byte(val, 2).hash()); + assert!(val.hash() != zero_byte(val, 3).hash()); + + fn zero_byte(val: u32, byte: uint) -> u32 { + assert!(byte < 4); + val & !(0xff << (byte * 8)) + } + } +} diff --git a/src/libstd/hashmap.rs b/src/libstd/hashmap.rs new file mode 100644 index 00000000000..e6ccb7a1d6b --- /dev/null +++ b/src/libstd/hashmap.rs @@ -0,0 +1,989 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! An unordered map and set type implemented as hash tables +//! +//! The tables use a keyed hash with new random keys generated for each container, so the ordering +//! of a set of keys in a hash table is randomized. + +use container::{Container, Mutable, Map, Set}; +use cmp::{Eq, Equiv}; +use hash::Hash; +use old_iter::BaseIter; +use old_iter; +use option::{None, Option, Some}; +use rand::RngUtil; +use rand; +use uint; +use vec; +use kinds::Copy; +use util::{replace, unreachable}; + +static INITIAL_CAPACITY: uint = 32u; // 2^5 + +struct Bucket<K,V> { + hash: uint, + key: K, + value: V, +} + +pub struct HashMap<K,V> { + priv k0: u64, + priv k1: u64, + priv resize_at: uint, + priv size: uint, + priv buckets: ~[Option<Bucket<K, V>>], +} + +// We could rewrite FoundEntry to have type Option<&Bucket<K, V>> +// which would be nifty +enum SearchResult { + FoundEntry(uint), FoundHole(uint), TableFull +} + +#[inline(always)] +fn resize_at(capacity: uint) -> uint { + ((capacity as float) * 3. / 4.) as uint +} + +pub fn linear_map_with_capacity<K:Eq + Hash,V>( + initial_capacity: uint) -> HashMap<K, V> { + let mut r = rand::task_rng(); + linear_map_with_capacity_and_keys(r.gen(), r.gen(), + initial_capacity) +} + +fn linear_map_with_capacity_and_keys<K:Eq + Hash,V>( + k0: u64, k1: u64, + initial_capacity: uint) -> HashMap<K, V> { + HashMap { + k0: k0, k1: k1, + resize_at: resize_at(initial_capacity), + size: 0, + buckets: vec::from_fn(initial_capacity, |_| None) + } +} + +priv impl<K:Hash + Eq,V> HashMap<K, V> { + #[inline(always)] + fn to_bucket(&self, h: uint) -> uint { + // A good hash function with entropy spread over all of the + // bits is assumed. SipHash is more than good enough. + h % self.buckets.len() + } + + #[inline(always)] + fn next_bucket(&self, idx: uint, len_buckets: uint) -> uint { + let n = (idx + 1) % len_buckets; + debug!("next_bucket(%?, %?) = %?", idx, len_buckets, n); + n + } + + #[inline(always)] + fn bucket_sequence(&self, hash: uint, + op: &fn(uint) -> bool) -> bool { + let start_idx = self.to_bucket(hash); + let len_buckets = self.buckets.len(); + let mut idx = start_idx; + loop { + if !op(idx) { return false; } + idx = self.next_bucket(idx, len_buckets); + if idx == start_idx { + return true; + } + } + } + + #[inline(always)] + fn bucket_for_key(&self, k: &K) -> SearchResult { + let hash = k.hash_keyed(self.k0, self.k1) as uint; + self.bucket_for_key_with_hash(hash, k) + } + + #[inline(always)] + fn bucket_for_key_equiv<Q:Hash + Equiv<K>>(&self, k: &Q) + -> SearchResult { + let hash = k.hash_keyed(self.k0, self.k1) as uint; + self.bucket_for_key_with_hash_equiv(hash, k) + } + + #[inline(always)] + fn bucket_for_key_with_hash(&self, + hash: uint, + k: &K) + -> SearchResult { + for self.bucket_sequence(hash) |i| { + match self.buckets[i] { + Some(ref bkt) => if bkt.hash == hash && *k == bkt.key { + return FoundEntry(i); + }, + None => return FoundHole(i) + } + } + TableFull + } + + #[inline(always)] + fn bucket_for_key_with_hash_equiv<Q:Equiv<K>>(&self, + hash: uint, + k: &Q) + -> SearchResult { + for self.bucket_sequence(hash) |i| { + match self.buckets[i] { + Some(ref bkt) => { + if bkt.hash == hash && k.equiv(&bkt.key) { + return FoundEntry(i); + } + }, + None => return FoundHole(i) + } + } + TableFull + } + + /// Expand the capacity of the array to the next power of two + /// and re-insert each of the existing buckets. + #[inline(always)] + fn expand(&mut self) { + let new_capacity = self.buckets.len() * 2; + self.resize(new_capacity); + } + + /// Expands the capacity of the array and re-insert each of the + /// existing buckets. + fn resize(&mut self, new_capacity: uint) { + self.resize_at = resize_at(new_capacity); + + let old_buckets = replace(&mut self.buckets, + vec::from_fn(new_capacity, |_| None)); + + self.size = 0; + do vec::consume(old_buckets) |_, bucket| { + self.insert_opt_bucket(bucket); + } + } + + fn insert_opt_bucket(&mut self, bucket: Option<Bucket<K, V>>) { + match bucket { + Some(Bucket{hash: hash, key: key, value: value}) => { + self.insert_internal(hash, key, value); + } + None => {} + } + } + + #[inline(always)] + fn value_for_bucket<'a>(&'a self, idx: uint) -> &'a V { + match self.buckets[idx] { + Some(ref bkt) => &bkt.value, + None => fail!("HashMap::find: internal logic error"), + } + } + + #[inline(always)] + fn mut_value_for_bucket<'a>(&'a mut self, idx: uint) -> &'a mut V { + match self.buckets[idx] { + Some(ref mut bkt) => &mut bkt.value, + None => unreachable() + } + } + + /// Inserts the key value pair into the buckets. + /// Assumes that there will be a bucket. + /// True if there was no previous entry with that key + fn insert_internal(&mut self, hash: uint, k: K, v: V) -> Option<V> { + match self.bucket_for_key_with_hash(hash, &k) { + TableFull => { fail!("Internal logic error"); } + FoundHole(idx) => { + debug!("insert fresh (%?->%?) at idx %?, hash %?", + k, v, idx, hash); + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + None + } + FoundEntry(idx) => { + debug!("insert overwrite (%?->%?) at idx %?, hash %?", + k, v, idx, hash); + match self.buckets[idx] { + None => { fail!("insert_internal: Internal logic error") } + Some(ref mut b) => { + b.hash = hash; + b.key = k; + Some(replace(&mut b.value, v)) + } + } + } + } + } + + fn pop_internal(&mut self, hash: uint, k: &K) -> Option<V> { + // Removing from an open-addressed hashtable + // is, well, painful. The problem is that + // the entry may lie on the probe path for other + // entries, so removing it would make you think that + // those probe paths are empty. + // + // To address this we basically have to keep walking, + // re-inserting entries we find until we reach an empty + // bucket. We know we will eventually reach one because + // we insert one ourselves at the beginning (the removed + // entry). + // + // I found this explanation elucidating: + // http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf + let mut idx = match self.bucket_for_key_with_hash(hash, k) { + TableFull | FoundHole(_) => return None, + FoundEntry(idx) => idx + }; + + let len_buckets = self.buckets.len(); + let bucket = replace(&mut self.buckets[idx], None); + + let value = match bucket { + None => None, + Some(Bucket{value, _}) => { + Some(value) + }, + }; + + /* re-inserting buckets may cause changes in size, so remember + what our new size is ahead of time before we start insertions */ + let size = self.size - 1; + idx = self.next_bucket(idx, len_buckets); + while self.buckets[idx].is_some() { + let bucket = replace(&mut self.buckets[idx], None); + self.insert_opt_bucket(bucket); + idx = self.next_bucket(idx, len_buckets); + } + self.size = size; + + value + } + + fn search(&self, hash: uint, + op: &fn(x: &Option<Bucket<K, V>>) -> bool) { + let _ = self.bucket_sequence(hash, |i| op(&self.buckets[i])); + } +} + +impl<K:Hash + Eq,V> Container for HashMap<K, V> { + /// Return the number of elements in the map + fn len(&const self) -> uint { self.size } + + /// Return true if the map contains no elements + fn is_empty(&const self) -> bool { self.len() == 0 } +} + +impl<K:Hash + Eq,V> Mutable for HashMap<K, V> { + /// Clear the map, removing all key-value pairs. + fn clear(&mut self) { + for uint::range(0, self.buckets.len()) |idx| { + self.buckets[idx] = None; + } + self.size = 0; + } +} + +impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> { + /// Return true if the map contains a value for the specified key + fn contains_key(&self, k: &K) -> bool { + match self.bucket_for_key(k) { + FoundEntry(_) => {true} + TableFull | FoundHole(_) => {false} + } + } + + /// Visit all key-value pairs + fn each<'a>(&'a self, blk: &fn(&K, &'a V) -> bool) -> bool { + for self.buckets.each |bucket| { + for bucket.each |pair| { + if !blk(&pair.key, &pair.value) { + return false; + } + } + } + return true; + } + + /// Visit all keys + fn each_key(&self, blk: &fn(k: &K) -> bool) -> bool { + self.each(|k, _| blk(k)) + } + + /// Visit all values + fn each_value<'a>(&'a self, blk: &fn(v: &'a V) -> bool) -> bool { + self.each(|_, v| blk(v)) + } + + /// Iterate over the map and mutate the contained values + fn mutate_values(&mut self, blk: &fn(&K, &mut V) -> bool) -> bool { + for uint::range(0, self.buckets.len()) |i| { + match self.buckets[i] { + Some(Bucket{key: ref key, value: ref mut value, _}) => { + if !blk(key, value) { return false; } + } + None => () + } + } + return true; + } + + /// Return a reference to the value corresponding to the key + fn find<'a>(&'a self, k: &K) -> Option<&'a V> { + match self.bucket_for_key(k) { + FoundEntry(idx) => Some(self.value_for_bucket(idx)), + TableFull | FoundHole(_) => None, + } + } + + /// Return a mutable reference to the value corresponding to the key + fn find_mut<'a>(&'a mut self, k: &K) -> Option<&'a mut V> { + let idx = match self.bucket_for_key(k) { + FoundEntry(idx) => idx, + TableFull | FoundHole(_) => return None + }; + Some(self.mut_value_for_bucket(idx)) + } + + /// Insert a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Return true if the key did + /// not already exist in the map. + fn insert(&mut self, k: K, v: V) -> bool { + self.swap(k, v).is_none() + } + + /// Remove a key-value pair from the map. Return true if the key + /// was present in the map, otherwise false. + fn remove(&mut self, k: &K) -> bool { + self.pop(k).is_some() + } + + /// Insert a key-value pair from the map. If the key already had a value + /// present in the map, that value is returned. Otherwise None is returned. + fn swap(&mut self, k: K, v: V) -> Option<V> { + // this could be faster. + + if self.size >= self.resize_at { + // n.b.: We could also do this after searching, so + // that we do not resize if this call to insert is + // simply going to update a key in place. My sense + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + self.insert_internal(hash, k, v) + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + fn pop(&mut self, k: &K) -> Option<V> { + let hash = k.hash_keyed(self.k0, self.k1) as uint; + self.pop_internal(hash, k) + } +} + +pub impl<K: Hash + Eq, V> HashMap<K, V> { + /// Create an empty HashMap + fn new() -> HashMap<K, V> { + HashMap::with_capacity(INITIAL_CAPACITY) + } + + /// Create an empty HashMap with space for at least `n` elements in + /// the hash table. + fn with_capacity(capacity: uint) -> HashMap<K, V> { + linear_map_with_capacity(capacity) + } + + /// Reserve space for at least `n` elements in the hash table. + fn reserve_at_least(&mut self, n: uint) { + if n > self.buckets.len() { + let buckets = n * 4 / 3 + 1; + self.resize(uint::next_power_of_two(buckets)); + } + } + + /// Return the value corresponding to the key in the map, or insert + /// and return the value if it doesn't exist. + fn find_or_insert<'a>(&'a mut self, k: K, v: V) -> &'a V { + if self.size >= self.resize_at { + // n.b.: We could also do this after searching, so + // that we do not resize if this call to insert is + // simply going to update a key in place. My sense + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => fail!("Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + self.value_for_bucket(idx) + } + + /// Return the value corresponding to the key in the map, or create, + /// insert, and return a new value if it doesn't exist. + fn find_or_insert_with<'a>(&'a mut self, k: K, f: &fn(&K) -> V) -> &'a V { + if self.size >= self.resize_at { + // n.b.: We could also do this after searching, so + // that we do not resize if this call to insert is + // simply going to update a key in place. My sense + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => fail!("Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + let v = f(&k); + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + self.value_for_bucket(idx) + } + + fn consume(&mut self, f: &fn(K, V)) { + let buckets = replace(&mut self.buckets, ~[]); + self.size = 0; + + do vec::consume(buckets) |_, bucket| { + match bucket { + None => {}, + Some(Bucket{key, value, _}) => { + f(key, value) + } + } + } + } + + fn get<'a>(&'a self, k: &K) -> &'a V { + match self.find(k) { + Some(v) => v, + None => fail!("No entry found for key: %?", k), + } + } + + /// Return true if the map contains a value for the specified key, + /// using equivalence + fn contains_key_equiv<Q:Hash + Equiv<K>>(&self, key: &Q) -> bool { + match self.bucket_for_key_equiv(key) { + FoundEntry(_) => {true} + TableFull | FoundHole(_) => {false} + } + } + + /// Return the value corresponding to the key in the map, using + /// equivalence + fn find_equiv<'a, Q:Hash + Equiv<K>>(&'a self, k: &Q) -> Option<&'a V> { + match self.bucket_for_key_equiv(k) { + FoundEntry(idx) => Some(self.value_for_bucket(idx)), + TableFull | FoundHole(_) => None, + } + } +} + +pub impl<K: Hash + Eq, V: Copy> HashMap<K, V> { + /// Like `find`, but returns a copy of the value. + fn find_copy(&self, k: &K) -> Option<V> { + self.find(k).map_consume(|v| copy *v) + } + + /// Like `get`, but returns a copy of the value. + fn get_copy(&self, k: &K) -> V { + copy *self.get(k) + } +} + +impl<K:Hash + Eq,V:Eq> Eq for HashMap<K, V> { + fn eq(&self, other: &HashMap<K, V>) -> bool { + if self.len() != other.len() { return false; } + + for self.each |key, value| { + match other.find(key) { + None => return false, + Some(v) => if value != v { return false }, + } + } + + true + } + + fn ne(&self, other: &HashMap<K, V>) -> bool { !self.eq(other) } +} + +pub struct HashSet<T> { + priv map: HashMap<T, ()> +} + +impl<T:Hash + Eq> BaseIter<T> for HashSet<T> { + /// Visit all values in order + fn each(&self, f: &fn(&T) -> bool) -> bool { self.map.each_key(f) } + fn size_hint(&self) -> Option<uint> { Some(self.len()) } +} + +impl<T:Hash + Eq> Eq for HashSet<T> { + fn eq(&self, other: &HashSet<T>) -> bool { self.map == other.map } + fn ne(&self, other: &HashSet<T>) -> bool { self.map != other.map } +} + +impl<T:Hash + Eq> Container for HashSet<T> { + /// Return the number of elements in the set + fn len(&const self) -> uint { self.map.len() } + + /// Return true if the set contains no elements + fn is_empty(&const self) -> bool { self.map.is_empty() } +} + +impl<T:Hash + Eq> Mutable for HashSet<T> { + /// Clear the set, removing all values. + fn clear(&mut self) { self.map.clear() } +} + +impl<T:Hash + Eq> Set<T> for HashSet<T> { + /// Return true if the set contains a value + fn contains(&self, value: &T) -> bool { self.map.contains_key(value) } + + /// Add a value to the set. Return true if the value was not already + /// present in the set. + fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()) } + + /// Remove a value from the set. Return true if the value was + /// present in the set. + fn remove(&mut self, value: &T) -> bool { self.map.remove(value) } + + /// Return true if the set has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + fn is_disjoint(&self, other: &HashSet<T>) -> bool { + old_iter::all(self, |v| !other.contains(v)) + } + + /// Return true if the set is a subset of another + fn is_subset(&self, other: &HashSet<T>) -> bool { + old_iter::all(self, |v| other.contains(v)) + } + + /// Return true if the set is a superset of another + fn is_superset(&self, other: &HashSet<T>) -> bool { + other.is_subset(self) + } + + /// Visit the values representing the difference + fn difference(&self, other: &HashSet<T>, f: &fn(&T) -> bool) -> bool { + self.each(|v| other.contains(v) || f(v)) + } + + /// Visit the values representing the symmetric difference + fn symmetric_difference(&self, + other: &HashSet<T>, + f: &fn(&T) -> bool) -> bool { + self.difference(other, f) && other.difference(self, f) + } + + /// Visit the values representing the intersection + fn intersection(&self, other: &HashSet<T>, f: &fn(&T) -> bool) -> bool { + self.each(|v| !other.contains(v) || f(v)) + } + + /// Visit the values representing the union + fn union(&self, other: &HashSet<T>, f: &fn(&T) -> bool) -> bool { + self.each(f) && other.each(|v| self.contains(v) || f(v)) + } +} + +pub impl <T:Hash + Eq> HashSet<T> { + /// Create an empty HashSet + fn new() -> HashSet<T> { + HashSet::with_capacity(INITIAL_CAPACITY) + } + + /// Create an empty HashSet with space for at least `n` elements in + /// the hash table. + fn with_capacity(capacity: uint) -> HashSet<T> { + HashSet { map: HashMap::with_capacity(capacity) } + } + + /// Reserve space for at least `n` elements in the hash table. + fn reserve_at_least(&mut self, n: uint) { + self.map.reserve_at_least(n) + } + + /// Consumes all of the elements in the set, emptying it out + fn consume(&mut self, f: &fn(T)) { + self.map.consume(|k, _| f(k)) + } + + fn contains_equiv<Q:Hash + Equiv<T>>(&self, value: &Q) -> bool { + self.map.contains_key_equiv(value) + } +} + +#[cfg(test)] +mod test_map { + use container::{Container, Map, Set}; + use option::{None, Some}; + use super::*; + use uint; + + #[test] + fn test_insert() { + let mut m = HashMap::new(); + assert!(m.insert(1, 2)); + assert!(m.insert(2, 4)); + assert_eq!(*m.get(&1), 2); + assert_eq!(*m.get(&2), 4); + } + + #[test] + fn test_find_mut() { + let mut m = HashMap::new(); + assert!(m.insert(1, 12)); + assert!(m.insert(2, 8)); + assert!(m.insert(5, 14)); + let new = 100; + match m.find_mut(&5) { + None => fail!(), Some(x) => *x = new + } + assert_eq!(m.find(&5), Some(&new)); + } + + #[test] + fn test_insert_overwrite() { + let mut m = HashMap::new(); + assert!(m.insert(1, 2)); + assert_eq!(*m.get(&1), 2); + assert!(!m.insert(1, 3)); + assert_eq!(*m.get(&1), 3); + } + + #[test] + fn test_insert_conflicts() { + let mut m = linear_map_with_capacity(4); + assert!(m.insert(1, 2)); + assert!(m.insert(5, 3)); + assert!(m.insert(9, 4)); + assert_eq!(*m.get(&9), 4); + assert_eq!(*m.get(&5), 3); + assert_eq!(*m.get(&1), 2); + } + + #[test] + fn test_conflict_remove() { + let mut m = linear_map_with_capacity(4); + assert!(m.insert(1, 2)); + assert!(m.insert(5, 3)); + assert!(m.insert(9, 4)); + assert!(m.remove(&1)); + assert_eq!(*m.get(&9), 4); + assert_eq!(*m.get(&5), 3); + } + + #[test] + fn test_is_empty() { + let mut m = linear_map_with_capacity(4); + assert!(m.insert(1, 2)); + assert!(!m.is_empty()); + assert!(m.remove(&1)); + assert!(m.is_empty()); + } + + #[test] + fn test_pop() { + let mut m = HashMap::new(); + m.insert(1, 2); + assert_eq!(m.pop(&1), Some(2)); + assert_eq!(m.pop(&1), None); + } + + #[test] + fn test_swap() { + let mut m = HashMap::new(); + assert_eq!(m.swap(1, 2), None); + assert_eq!(m.swap(1, 3), Some(2)); + assert_eq!(m.swap(1, 4), Some(3)); + } + + #[test] + fn test_find_or_insert() { + let mut m = HashMap::new::<int, int>(); + assert_eq!(m.find_or_insert(1, 2), &2); + assert_eq!(m.find_or_insert(1, 3), &2); + } + + #[test] + fn test_find_or_insert_with() { + let mut m = HashMap::new::<int, int>(); + assert_eq!(m.find_or_insert_with(1, |_| 2), &2); + assert_eq!(m.find_or_insert_with(1, |_| 3), &2); + } + + #[test] + fn test_consume() { + let mut m = HashMap::new(); + assert!(m.insert(1, 2)); + assert!(m.insert(2, 3)); + let mut m2 = HashMap::new(); + do m.consume |k, v| { + m2.insert(k, v); + } + assert_eq!(m.len(), 0); + assert_eq!(m2.len(), 2); + assert_eq!(m2.get(&1), &2); + assert_eq!(m2.get(&2), &3); + } + + #[test] + fn test_iterate() { + let mut m = linear_map_with_capacity(4); + for uint::range(0, 32) |i| { + assert!(m.insert(i, i*2)); + } + let mut observed = 0; + for m.each |k, v| { + assert_eq!(*v, *k * 2); + observed |= (1 << *k); + } + assert_eq!(observed, 0xFFFF_FFFF); + } + + #[test] + fn test_find() { + let mut m = HashMap::new(); + assert!(m.find(&1).is_none()); + m.insert(1, 2); + match m.find(&1) { + None => fail!(), + Some(v) => assert!(*v == 2) + } + } + + #[test] + fn test_eq() { + let mut m1 = HashMap::new(); + m1.insert(1, 2); + m1.insert(2, 3); + m1.insert(3, 4); + + let mut m2 = HashMap::new(); + m2.insert(1, 2); + m2.insert(2, 3); + + assert!(m1 != m2); + + m2.insert(3, 4); + + assert_eq!(m1, m2); + } + + #[test] + fn test_expand() { + let mut m = HashMap::new(); + + assert_eq!(m.len(), 0); + assert!(m.is_empty()); + + let mut i = 0u; + let old_resize_at = m.resize_at; + while old_resize_at == m.resize_at { + m.insert(i, i); + i += 1; + } + + assert_eq!(m.len(), i); + assert!(!m.is_empty()); + } +} + +#[cfg(test)] +mod test_set { + use super::*; + use container::{Container, Map, Set}; + use vec; + + #[test] + fn test_disjoint() { + let mut xs = HashSet::new(); + let mut ys = HashSet::new(); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(xs.insert(5)); + assert!(ys.insert(11)); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(xs.insert(7)); + assert!(xs.insert(19)); + assert!(xs.insert(4)); + assert!(ys.insert(2)); + assert!(ys.insert(-11)); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(ys.insert(7)); + assert!(!xs.is_disjoint(&ys)); + assert!(!ys.is_disjoint(&xs)); + } + + #[test] + fn test_subset_and_superset() { + let mut a = HashSet::new(); + assert!(a.insert(0)); + assert!(a.insert(5)); + assert!(a.insert(11)); + assert!(a.insert(7)); + + let mut b = HashSet::new(); + assert!(b.insert(0)); + assert!(b.insert(7)); + assert!(b.insert(19)); + assert!(b.insert(250)); + assert!(b.insert(11)); + assert!(b.insert(200)); + + assert!(!a.is_subset(&b)); + assert!(!a.is_superset(&b)); + assert!(!b.is_subset(&a)); + assert!(!b.is_superset(&a)); + + assert!(b.insert(5)); + + assert!(a.is_subset(&b)); + assert!(!a.is_superset(&b)); + assert!(!b.is_subset(&a)); + assert!(b.is_superset(&a)); + } + + #[test] + fn test_intersection() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(11)); + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(77)); + assert!(a.insert(103)); + assert!(a.insert(5)); + assert!(a.insert(-5)); + + assert!(b.insert(2)); + assert!(b.insert(11)); + assert!(b.insert(77)); + assert!(b.insert(-9)); + assert!(b.insert(-42)); + assert!(b.insert(5)); + assert!(b.insert(3)); + + let mut i = 0; + let expected = [3, 5, 11, 77]; + for a.intersection(&b) |x| { + assert!(vec::contains(expected, x)); + i += 1 + } + assert_eq!(i, expected.len()); + } + + #[test] + fn test_difference() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + + assert!(b.insert(3)); + assert!(b.insert(9)); + + let mut i = 0; + let expected = [1, 5, 11]; + for a.difference(&b) |x| { + assert!(vec::contains(expected, x)); + i += 1 + } + assert_eq!(i, expected.len()); + } + + #[test] + fn test_symmetric_difference() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + + assert!(b.insert(-2)); + assert!(b.insert(3)); + assert!(b.insert(9)); + assert!(b.insert(14)); + assert!(b.insert(22)); + + let mut i = 0; + let expected = [-2, 1, 5, 11, 14, 22]; + for a.symmetric_difference(&b) |x| { + assert!(vec::contains(expected, x)); + i += 1 + } + assert_eq!(i, expected.len()); + } + + #[test] + fn test_union() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + assert!(a.insert(16)); + assert!(a.insert(19)); + assert!(a.insert(24)); + + assert!(b.insert(-2)); + assert!(b.insert(1)); + assert!(b.insert(5)); + assert!(b.insert(9)); + assert!(b.insert(13)); + assert!(b.insert(19)); + + let mut i = 0; + let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24]; + for a.union(&b) |x| { + assert!(vec::contains(expected, x)); + i += 1 + } + assert_eq!(i, expected.len()); + } +} diff --git a/src/libstd/io.rs b/src/libstd/io.rs new file mode 100644 index 00000000000..aa312742e3e --- /dev/null +++ b/src/libstd/io.rs @@ -0,0 +1,2105 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +The `io` module contains basic input and output routines. + +A quick summary: + +## `Reader` and `Writer` traits + +These traits define the minimal set of methods that anything that can do +input and output should implement. + +## `ReaderUtil` and `WriterUtil` traits + +Richer methods that allow you to do more. `Reader` only lets you read a certain +number of bytes into a buffer, while `ReaderUtil` allows you to read a whole +line, for example. + +Generally, these richer methods are probably the ones you want to actually +use in day-to-day Rust. + +Furthermore, because there is an implementation of `ReaderUtil` for +`<T: Reader>`, when your input or output code implements `Reader`, you get +all of these methods for free. + +## `print` and `println` + +These very useful functions are defined here. You generally don't need to +import them, though, as the prelude already does. + +## `stdin`, `stdout`, and `stderr` + +These functions return references to the classic three file descriptors. They +implement `Reader` and `Writer`, where appropriate. + +*/ + +use result::Result; + +use container::Container; +use int; +use libc; +use libc::{c_int, c_long, c_void, size_t, ssize_t}; +use libc::consts::os::posix88::*; +use os; +use cast; +use path::Path; +use ops::Drop; +use old_iter::{BaseIter, CopyableIter}; +use ptr; +use result; +use str; +use str::StrSlice; +use to_str::ToStr; +use uint; +use vec; +use vec::{OwnedVector, OwnedCopyableVector}; + +#[allow(non_camel_case_types)] // not sure what to do about this +pub type fd_t = c_int; + +pub mod rustrt { + use libc; + + #[abi = "cdecl"] + #[link_name = "rustrt"] + pub extern { + unsafe fn rust_get_stdin() -> *libc::FILE; + unsafe fn rust_get_stdout() -> *libc::FILE; + unsafe fn rust_get_stderr() -> *libc::FILE; + } +} + +// Reading + +// FIXME (#2004): This is all buffered. We might need an unbuffered variant +// as well +/** +* The SeekStyle enum describes the relationship between the position +* we'd like to seek to from our current position. It's used as an argument +* to the `seek` method defined on the `Reader` trait. +* +* There are three seek styles: +* +* 1. `SeekSet` means that the new position should become our position. +* 2. `SeekCur` means that we should seek from the current position. +* 3. `SeekEnd` means that we should seek from the end. +* +* # Examples +* +* None right now. +*/ +pub enum SeekStyle { SeekSet, SeekEnd, SeekCur, } + + +/** +* The core Reader trait. All readers must implement this trait. +* +* # Examples +* +* None right now. +*/ +pub trait Reader { + // FIXME (#2004): Seekable really should be orthogonal. + + // FIXME (#2982): This should probably return an error. + /** + * Reads bytes and puts them into `bytes`, advancing the cursor. Returns the + * number of bytes read. + * + * The number of bytes to be read is `len` or the end of the file, + * whichever comes first. + * + * The buffer must be at least `len` bytes long. + * + * `read` is conceptually similar to C's `fread` function. + * + * # Examples + * + * None right now. + */ + fn read(&self, bytes: &mut [u8], len: uint) -> uint; + + /** + * Reads a single byte, advancing the cursor. + * + * In the case of an EOF or an error, returns a negative value. + * + * `read_byte` is conceptually similar to C's `getc` function. + * + * # Examples + * + * None right now. + */ + fn read_byte(&self) -> int; + + /** + * Returns a boolean value: are we currently at EOF? + * + * `eof` is conceptually similar to C's `feof` function. + * + * # Examples + * + * None right now. + */ + fn eof(&self) -> bool; + + /** + * Seek to a given `position` in the stream. + * + * Takes an optional SeekStyle, which affects how we seek from the + * position. See `SeekStyle` docs for more details. + * + * `seek` is conceptually similar to C's `fseek` function. + * + * # Examples + * + * None right now. + */ + fn seek(&self, position: int, style: SeekStyle); + + /** + * Returns the current position within the stream. + * + * `tell` is conceptually similar to C's `ftell` function. + * + * # Examples + * + * None right now. + */ + fn tell(&self) -> uint; +} + +impl Reader for @Reader { + fn read(&self, bytes: &mut [u8], len: uint) -> uint { + self.read(bytes, len) + } + fn read_byte(&self) -> int { + self.read_byte() + } + fn eof(&self) -> bool { + self.eof() + } + fn seek(&self, position: int, style: SeekStyle) { + self.seek(position, style) + } + fn tell(&self) -> uint { + self.tell() + } +} + +/** +* The `ReaderUtil` trait is a home for many of the utility functions +* a particular Reader should implement. +* +* The default `Reader` trait is focused entirely on bytes. `ReaderUtil` is based +* on higher-level concepts like 'chars' and 'lines.' +* +* # Examples: +* +* None right now. +*/ +pub trait ReaderUtil { + + /** + * Reads `len` number of bytes, and gives you a new vector back. + * + * # Examples + * + * None right now. + */ + fn read_bytes(&self, len: uint) -> ~[u8]; + + /** + * Reads up until a specific byte is seen or EOF. + * + * The `include` parameter specifies if the character should be included + * in the returned string. + * + * # Examples + * + * None right now. + */ + fn read_until(&self, c: u8, include: bool) -> ~str; + + /** + * Reads up until the first '\n' or EOF. + * + * The '\n' is not included in the result. + * + * # Examples + * + * None right now. + */ + fn read_line(&self) -> ~str; + + /** + * Reads `n` chars. + * + * Assumes that those chars are UTF-8 encoded. + * + * The '\n' is not included in the result. + * + * # Examples + * + * None right now. + */ + fn read_chars(&self, n: uint) -> ~[char]; + + /** + * Reads a single UTF-8 encoded char. + * + * # Examples + * + * None right now. + */ + fn read_char(&self) -> char; + + /** + * Reads up until the first null byte or EOF. + * + * The null byte is not returned. + * + * # Examples + * + * None right now. + */ + fn read_c_str(&self) -> ~str; + + /** + * Reads all remaining data in the stream. + * + * # Examples + * + * None right now. + */ + fn read_whole_stream(&self) -> ~[u8]; + + /** + * Iterate over every byte until EOF or the iterator breaks. + * + * # Examples + * + * None right now. + */ + fn each_byte(&self, it: &fn(int) -> bool) -> bool; + + /** + * Iterate over every char until EOF or the iterator breaks. + * + * # Examples + * + * None right now. + */ + fn each_char(&self, it: &fn(char) -> bool) -> bool; + + /** + * Iterate over every line until EOF or the iterator breaks. + * + * # Examples + * + * None right now. + */ + fn each_line(&self, it: &fn(&str) -> bool) -> bool; + + /** + * Reads all of the lines in the stream. + * + * Returns a vector of those lines. + * + * # Examples + * + * None right now. + */ + fn read_lines(&self) -> ~[~str]; + + /** + * Reads `n` little-endian unsigned integer bytes. + * + * `n` must be between 1 and 8, inclusive. + * + * # Examples + * + * None right now. + */ + fn read_le_uint_n(&self, nbytes: uint) -> u64; + + /** + * Reads `n` little-endian signed integer bytes. + * + * `n` must be between 1 and 8, inclusive. + * + * # Examples + * + * None right now. + */ + fn read_le_int_n(&self, nbytes: uint) -> i64; + + /** + * Reads `n` big-endian unsigned integer bytes. + * + * `n` must be between 1 and 8, inclusive. + * + * # Examples + * + * None right now. + */ + fn read_be_uint_n(&self, nbytes: uint) -> u64; + + /** + * Reads `n` big-endian signed integer bytes. + * + * `n` must be between 1 and 8, inclusive. + * + * # Examples + * + * None right now. + */ + fn read_be_int_n(&self, nbytes: uint) -> i64; + + /** + * Reads a little-endian unsigned integer. + * + * The number of bytes returned is system-dependant. + * + * # Examples + * + * None right now. + */ + fn read_le_uint(&self) -> uint; + + /** + * Reads a little-endian integer. + * + * The number of bytes returned is system-dependant. + * + * # Examples + * + * None right now. + */ + fn read_le_int(&self) -> int; + + /** + * Reads a big-endian unsigned integer. + * + * The number of bytes returned is system-dependant. + * + * # Examples + * + * None right now. + */ + fn read_be_uint(&self) -> uint; + + /** + * Reads a big-endian integer. + * + * The number of bytes returned is system-dependant. + * + * # Examples + * + * None right now. + */ + fn read_be_int(&self) -> int; + + /** + * Reads a big-endian `u64`. + * + * `u64`s are 8 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_u64(&self) -> u64; + + /** + * Reads a big-endian `u32`. + * + * `u32`s are 4 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_u32(&self) -> u32; + + /** + * Reads a big-endian `u16`. + * + * `u16`s are 2 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_u16(&self) -> u16; + + /** + * Reads a big-endian `i64`. + * + * `i64`s are 8 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_i64(&self) -> i64; + + /** + * Reads a big-endian `i32`. + * + * `i32`s are 4 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_i32(&self) -> i32; + + /** + * Reads a big-endian `i16`. + * + * `i16`s are 2 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_be_i16(&self) -> i16; + + /** + * Reads a big-endian `f64`. + * + * `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + * + * # Examples + * + * None right now. + */ + fn read_be_f64(&self) -> f64; + + /** + * Reads a big-endian `f32`. + * + * `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + * + * # Examples + * + * None right now. + */ + fn read_be_f32(&self) -> f32; + + /** + * Reads a little-endian `u64`. + * + * `u64`s are 8 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_u64(&self) -> u64; + + /** + * Reads a little-endian `u32`. + * + * `u32`s are 4 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_u32(&self) -> u32; + + /** + * Reads a little-endian `u16`. + * + * `u16`s are 2 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_u16(&self) -> u16; + + /** + * Reads a little-endian `i64`. + * + * `i64`s are 8 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_i64(&self) -> i64; + + /** + * Reads a little-endian `i32`. + * + * `i32`s are 4 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_i32(&self) -> i32; + + /** + * Reads a little-endian `i16`. + * + * `i16`s are 2 bytes long. + * + * # Examples + * + * None right now. + */ + fn read_le_i16(&self) -> i16; + + /** + * Reads a little-endian `f64`. + * + * `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + * + * # Examples + * + * None right now. + */ + fn read_le_f64(&self) -> f64; + + /** + * Reads a little-endian `f32`. + * + * `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + * + * # Examples + * + * None right now. + */ + fn read_le_f32(&self) -> f32; + + /** + * Read a u8. + * + * `u8`s are 1 byte. + * + * # Examples + * + * None right now. + */ + fn read_u8(&self) -> u8; + + /** + * Read an i8. + * + * `i8`s are 1 byte. + * + * # Examples + * + * None right now. + */ + fn read_i8(&self) -> i8; +} + +impl<T:Reader> ReaderUtil for T { + + fn read_bytes(&self,len: uint) -> ~[u8] { + let mut bytes = vec::with_capacity(len); + unsafe { vec::raw::set_len(&mut bytes, len); } + + let count = self.read(bytes, len); + + unsafe { vec::raw::set_len(&mut bytes, count); } + bytes + } + + fn read_until(&self, c: u8, include: bool) -> ~str { + let mut bytes = ~[]; + loop { + let ch = self.read_byte(); + if ch == -1 || ch == c as int { + if include && ch == c as int { + bytes.push(ch as u8); + } + break; + } + bytes.push(ch as u8); + } + str::from_bytes(bytes) + } + + fn read_line(&self) -> ~str { + self.read_until('\n' as u8, false) + } + + fn read_chars(&self, n: uint) -> ~[char] { + // returns the (consumed offset, n_req), appends characters to &chars + fn chars_from_bytes<T:Reader>(bytes: &~[u8], chars: &mut ~[char]) + -> (uint, uint) { + let mut i = 0; + let bytes_len = bytes.len(); + while i < bytes_len { + let b0 = bytes[i]; + let w = str::utf8_char_width(b0); + let end = i + w; + i += 1; + assert!((w > 0)); + if w == 1 { + chars.push(b0 as char); + loop; + } + // can't satisfy this char with the existing data + if end > bytes_len { + return (i - 1, end - bytes_len); + } + let mut val = 0; + while i < end { + let next = bytes[i] as int; + i += 1; + assert!((next > -1)); + assert_eq!(next & 192, 128); + val <<= 6; + val += (next & 63) as uint; + } + // See str::char_at + val += ((b0 << ((w + 1) as u8)) as uint) + << (w - 1) * 6 - w - 1u; + chars.push(val as char); + } + return (i, 0); + } + let mut bytes = ~[]; + let mut chars = ~[]; + // might need more bytes, but reading n will never over-read + let mut nbread = n; + while nbread > 0 { + let data = self.read_bytes(nbread); + if data.is_empty() { + // eof - FIXME (#2004): should we do something if + // we're split in a unicode char? + break; + } + bytes.push_all(data); + let (offset, nbreq) = chars_from_bytes::<T>(&bytes, &mut chars); + let ncreq = n - chars.len(); + // again we either know we need a certain number of bytes + // to complete a character, or we make sure we don't + // over-read by reading 1-byte per char needed + nbread = if ncreq > nbreq { ncreq } else { nbreq }; + if nbread > 0 { + bytes = vec::slice(bytes, offset, bytes.len()).to_vec(); + } + } + chars + } + + fn read_char(&self) -> char { + let c = self.read_chars(1); + if c.len() == 0 { + return -1 as char; // FIXME will this stay valid? // #2004 + } + assert_eq!(c.len(), 1); + return c[0]; + } + + fn read_c_str(&self) -> ~str { + self.read_until(0u8, false) + } + + fn read_whole_stream(&self) -> ~[u8] { + let mut bytes: ~[u8] = ~[]; + while !self.eof() { bytes.push_all(self.read_bytes(2048u)); } + bytes + } + + fn each_byte(&self, it: &fn(int) -> bool) -> bool { + while !self.eof() { + if !it(self.read_byte()) { return false; } + } + return true; + } + + fn each_char(&self, it: &fn(char) -> bool) -> bool { + while !self.eof() { + if !it(self.read_char()) { return false; } + } + return true; + } + + fn each_line(&self, it: &fn(s: &str) -> bool) -> bool { + while !self.eof() { + // include the \n, so that we can distinguish an entirely empty + // line read after "...\n", and the trailing empty line in + // "...\n\n". + let mut line = self.read_until('\n' as u8, true); + + // blank line at the end of the reader is ignored + if self.eof() && line.is_empty() { break; } + + // trim the \n, so that each_line is consistent with read_line + let n = str::len(line); + if line[n-1] == '\n' as u8 { + unsafe { str::raw::set_len(&mut line, n-1); } + } + + if !it(line) { return false; } + } + return true; + } + + fn read_lines(&self) -> ~[~str] { + do vec::build |push| { + for self.each_line |line| { + push(str::to_owned(line)); + } + } + } + + // FIXME int reading methods need to deal with eof - issue #2004 + + fn read_le_uint_n(&self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, pos = 0, i = nbytes; + while i > 0 { + val += (self.read_u8() as u64) << pos; + pos += 8; + i -= 1; + } + val + } + + fn read_le_int_n(&self, nbytes: uint) -> i64 { + extend_sign(self.read_le_uint_n(nbytes), nbytes) + } + + fn read_be_uint_n(&self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, i = nbytes; + while i > 0 { + i -= 1; + val += (self.read_u8() as u64) << i * 8; + } + val + } + + fn read_be_int_n(&self, nbytes: uint) -> i64 { + extend_sign(self.read_be_uint_n(nbytes), nbytes) + } + + fn read_le_uint(&self) -> uint { + self.read_le_uint_n(uint::bytes) as uint + } + + fn read_le_int(&self) -> int { + self.read_le_int_n(int::bytes) as int + } + + fn read_be_uint(&self) -> uint { + self.read_be_uint_n(uint::bytes) as uint + } + + fn read_be_int(&self) -> int { + self.read_be_int_n(int::bytes) as int + } + + fn read_be_u64(&self) -> u64 { + self.read_be_uint_n(8) as u64 + } + + fn read_be_u32(&self) -> u32 { + self.read_be_uint_n(4) as u32 + } + + fn read_be_u16(&self) -> u16 { + self.read_be_uint_n(2) as u16 + } + + fn read_be_i64(&self) -> i64 { + self.read_be_int_n(8) as i64 + } + + fn read_be_i32(&self) -> i32 { + self.read_be_int_n(4) as i32 + } + + fn read_be_i16(&self) -> i16 { + self.read_be_int_n(2) as i16 + } + + fn read_be_f64(&self) -> f64 { + unsafe { + cast::transmute::<u64, f64>(self.read_be_u64()) + } + } + + fn read_be_f32(&self) -> f32 { + unsafe { + cast::transmute::<u32, f32>(self.read_be_u32()) + } + } + + fn read_le_u64(&self) -> u64 { + self.read_le_uint_n(8) as u64 + } + + fn read_le_u32(&self) -> u32 { + self.read_le_uint_n(4) as u32 + } + + fn read_le_u16(&self) -> u16 { + self.read_le_uint_n(2) as u16 + } + + fn read_le_i64(&self) -> i64 { + self.read_le_int_n(8) as i64 + } + + fn read_le_i32(&self) -> i32 { + self.read_le_int_n(4) as i32 + } + + fn read_le_i16(&self) -> i16 { + self.read_le_int_n(2) as i16 + } + + fn read_le_f64(&self) -> f64 { + unsafe { + cast::transmute::<u64, f64>(self.read_le_u64()) + } + } + + fn read_le_f32(&self) -> f32 { + unsafe { + cast::transmute::<u32, f32>(self.read_le_u32()) + } + } + + fn read_u8(&self) -> u8 { + self.read_byte() as u8 + } + + fn read_i8(&self) -> i8 { + self.read_byte() as i8 + } +} + +fn extend_sign(val: u64, nbytes: uint) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + +// Reader implementations + +fn convert_whence(whence: SeekStyle) -> i32 { + return match whence { + SeekSet => 0i32, + SeekCur => 1i32, + SeekEnd => 2i32 + }; +} + +impl Reader for *libc::FILE { + fn read(&self, bytes: &mut [u8], len: uint) -> uint { + unsafe { + do vec::as_mut_buf(bytes) |buf_p, buf_len| { + assert!(buf_len >= len); + + let count = libc::fread(buf_p as *mut c_void, 1u as size_t, + len as size_t, *self) as uint; + if count < len { + match libc::ferror(*self) { + 0 => (), + _ => { + error!("error reading buffer"); + error!("%s", os::last_os_error()); + fail!(); + } + } + } + + count + } + } + } + fn read_byte(&self) -> int { + unsafe { + libc::fgetc(*self) as int + } + } + fn eof(&self) -> bool { + unsafe { + return libc::feof(*self) != 0 as c_int; + } + } + fn seek(&self, offset: int, whence: SeekStyle) { + unsafe { + assert!(libc::fseek(*self, + offset as c_long, + convert_whence(whence)) == 0 as c_int); + } + } + fn tell(&self) -> uint { + unsafe { + return libc::ftell(*self) as uint; + } + } +} + +struct Wrapper<T, C> { + base: T, + cleanup: C, +} + +// A forwarding impl of reader that also holds on to a resource for the +// duration of its lifetime. +// FIXME there really should be a better way to do this // #2004 +impl<R:Reader,C> Reader for Wrapper<R, C> { + fn read(&self, bytes: &mut [u8], len: uint) -> uint { + self.base.read(bytes, len) + } + fn read_byte(&self) -> int { self.base.read_byte() } + fn eof(&self) -> bool { self.base.eof() } + fn seek(&self, off: int, whence: SeekStyle) { + self.base.seek(off, whence) + } + fn tell(&self) -> uint { self.base.tell() } +} + +pub struct FILERes { + f: *libc::FILE, +} + +impl Drop for FILERes { + fn finalize(&self) { + unsafe { + libc::fclose(self.f); + } + } +} + +pub fn FILERes(f: *libc::FILE) -> FILERes { + FILERes { + f: f + } +} + +pub fn FILE_reader(f: *libc::FILE, cleanup: bool) -> @Reader { + if cleanup { + @Wrapper { base: f, cleanup: FILERes(f) } as @Reader + } else { + @f as @Reader + } +} + +// FIXME (#2004): this should either be an trait-less impl, a set of +// top-level functions that take a reader, or a set of default methods on +// reader (which can then be called reader) + +/** +* Gives a `Reader` that allows you to read values from standard input. +* +* # Examples +* ~~~ +* let stdin = core::io::stdin(); +* let line = stdin.read_line(); +* core::io::print(line); +* ~~~ +*/ +pub fn stdin() -> @Reader { + unsafe { + @rustrt::rust_get_stdin() as @Reader + } +} + +pub fn file_reader(path: &Path) -> Result<@Reader, ~str> { + unsafe { + let f = os::as_c_charp(path.to_str(), |pathbuf| { + os::as_c_charp("r", |modebuf| + libc::fopen(pathbuf, modebuf) + ) + }); + return if f as uint == 0u { result::Err(~"error opening " + + path.to_str()) } + else { + result::Ok(FILE_reader(f, true)) + } + } +} + + +// Byte readers +pub struct BytesReader<'self> { + bytes: &'self [u8], + pos: @mut uint +} + +impl<'self> Reader for BytesReader<'self> { + fn read(&self, bytes: &mut [u8], len: uint) -> uint { + let count = uint::min(len, self.bytes.len() - *self.pos); + + let view = vec::slice(self.bytes, *self.pos, self.bytes.len()); + vec::bytes::copy_memory(bytes, view, count); + + *self.pos += count; + + count + } + + fn read_byte(&self) -> int { + if *self.pos == self.bytes.len() { + return -1; + } + + let b = self.bytes[*self.pos]; + *self.pos += 1u; + b as int + } + + fn eof(&self) -> bool { + *self.pos == self.bytes.len() + } + + fn seek(&self, offset: int, whence: SeekStyle) { + let pos = *self.pos; + *self.pos = seek_in_buf(offset, pos, self.bytes.len(), whence); + } + + fn tell(&self) -> uint { + *self.pos + } +} + +pub fn with_bytes_reader<T>(bytes: &[u8], f: &fn(@Reader) -> T) -> T { + f(@BytesReader { + bytes: bytes, + pos: @mut 0 + } as @Reader) +} + +pub fn with_str_reader<T>(s: &str, f: &fn(@Reader) -> T) -> T { + str::byte_slice(s, |bytes| with_bytes_reader(bytes, f)) +} + +// Writing +pub enum FileFlag { Append, Create, Truncate, NoFlag, } + +// What type of writer are we? +#[deriving(Eq)] +pub enum WriterType { Screen, File } + +// FIXME (#2004): Seekable really should be orthogonal. +// FIXME (#2004): eventually u64 +/// The raw underlying writer trait. All writers must implement this. +pub trait Writer { + + /// Write all of the given bytes. + fn write(&self, v: &[u8]); + + /// Move the current position within the stream. The second parameter + /// determines the position that the first parameter is relative to. + fn seek(&self, int, SeekStyle); + + /// Return the current position within the stream. + fn tell(&self) -> uint; + + /// Flush the output buffer for this stream (if there is one). + fn flush(&self) -> int; + + /// Determine if this Writer is writing to a file or not. + fn get_type(&self) -> WriterType; +} + +impl Writer for @Writer { + fn write(&self, v: &[u8]) { self.write(v) } + fn seek(&self, a: int, b: SeekStyle) { self.seek(a, b) } + fn tell(&self) -> uint { self.tell() } + fn flush(&self) -> int { self.flush() } + fn get_type(&self) -> WriterType { self.get_type() } +} + +impl<W:Writer,C> Writer for Wrapper<W, C> { + fn write(&self, bs: &[u8]) { self.base.write(bs); } + fn seek(&self, off: int, style: SeekStyle) { self.base.seek(off, style); } + fn tell(&self) -> uint { self.base.tell() } + fn flush(&self) -> int { self.base.flush() } + fn get_type(&self) -> WriterType { File } +} + +impl Writer for *libc::FILE { + fn write(&self, v: &[u8]) { + unsafe { + do vec::as_const_buf(v) |vbuf, len| { + let nout = libc::fwrite(vbuf as *c_void, + 1, + len as size_t, + *self); + if nout != len as size_t { + error!("error writing buffer"); + error!("%s", os::last_os_error()); + fail!(); + } + } + } + } + fn seek(&self, offset: int, whence: SeekStyle) { + unsafe { + assert!(libc::fseek(*self, + offset as c_long, + convert_whence(whence)) == 0 as c_int); + } + } + fn tell(&self) -> uint { + unsafe { + libc::ftell(*self) as uint + } + } + fn flush(&self) -> int { + unsafe { + libc::fflush(*self) as int + } + } + fn get_type(&self) -> WriterType { + unsafe { + let fd = libc::fileno(*self); + if libc::isatty(fd) == 0 { File } + else { Screen } + } + } +} + +pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer { + if cleanup { + @Wrapper { base: f, cleanup: FILERes(f) } as @Writer + } else { + @f as @Writer + } +} + +impl Writer for fd_t { + fn write(&self, v: &[u8]) { + unsafe { + let mut count = 0u; + do vec::as_const_buf(v) |vbuf, len| { + while count < len { + let vb = ptr::const_offset(vbuf, count) as *c_void; + let nout = libc::write(*self, vb, len as size_t); + if nout < 0 as ssize_t { + error!("error writing buffer"); + error!("%s", os::last_os_error()); + fail!(); + } + count += nout as uint; + } + } + } + } + fn seek(&self, _offset: int, _whence: SeekStyle) { + error!("need 64-bit foreign calls for seek, sorry"); + fail!(); + } + fn tell(&self) -> uint { + error!("need 64-bit foreign calls for tell, sorry"); + fail!(); + } + fn flush(&self) -> int { 0 } + fn get_type(&self) -> WriterType { + unsafe { + if libc::isatty(*self) == 0 { File } else { Screen } + } + } +} + +pub struct FdRes { + fd: fd_t, +} + +impl Drop for FdRes { + fn finalize(&self) { + unsafe { + libc::close(self.fd); + } + } +} + +pub fn FdRes(fd: fd_t) -> FdRes { + FdRes { + fd: fd + } +} + +pub fn fd_writer(fd: fd_t, cleanup: bool) -> @Writer { + if cleanup { + @Wrapper { base: fd, cleanup: FdRes(fd) } as @Writer + } else { + @fd as @Writer + } +} + + +pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) + -> Result<@Writer, ~str> { + #[cfg(windows)] + fn wb() -> c_int { + (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int + } + + #[cfg(unix)] + fn wb() -> c_int { O_WRONLY as c_int } + + let mut fflags: c_int = wb(); + for flags.each |f| { + match *f { + Append => fflags |= O_APPEND as c_int, + Create => fflags |= O_CREAT as c_int, + Truncate => fflags |= O_TRUNC as c_int, + NoFlag => () + } + } + let fd = unsafe { + do os::as_c_charp(path.to_str()) |pathbuf| { + libc::open(pathbuf, fflags, + (S_IRUSR | S_IWUSR) as c_int) + } + }; + if fd < (0 as c_int) { + result::Err(fmt!("error opening %s: %s", path.to_str(), + os::last_os_error())) + } else { + result::Ok(fd_writer(fd, true)) + } +} + +pub fn u64_to_le_bytes<T>(n: u64, size: uint, + f: &fn(v: &[u8]) -> T) -> T { + assert!(size <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(&[n as u8, + (n >> 8) as u8]), + 4u => f(&[n as u8, + (n >> 8) as u8, + (n >> 16) as u8, + (n >> 24) as u8]), + 8u => f(&[n as u8, + (n >> 8) as u8, + (n >> 16) as u8, + (n >> 24) as u8, + (n >> 32) as u8, + (n >> 40) as u8, + (n >> 48) as u8, + (n >> 56) as u8]), + _ => { + + let mut bytes: ~[u8] = ~[], i = size, n = n; + while i > 0u { + bytes.push((n & 255_u64) as u8); + n >>= 8_u64; + i -= 1u; + } + f(bytes) + } + } +} + +pub fn u64_to_be_bytes<T>(n: u64, size: uint, + f: &fn(v: &[u8]) -> T) -> T { + assert!(size <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(&[(n >> 8) as u8, + n as u8]), + 4u => f(&[(n >> 24) as u8, + (n >> 16) as u8, + (n >> 8) as u8, + n as u8]), + 8u => f(&[(n >> 56) as u8, + (n >> 48) as u8, + (n >> 40) as u8, + (n >> 32) as u8, + (n >> 24) as u8, + (n >> 16) as u8, + (n >> 8) as u8, + n as u8]), + _ => { + let mut bytes: ~[u8] = ~[]; + let mut i = size; + while i > 0u { + let shift = ((i - 1u) * 8u) as u64; + bytes.push((n >> shift) as u8); + i -= 1u; + } + f(bytes) + } + } +} + +pub fn u64_from_be_bytes(data: &[u8], + start: uint, + size: uint) + -> u64 { + let mut sz = size; + assert!((sz <= 8u)); + let mut val = 0_u64; + let mut pos = start; + while sz > 0u { + sz -= 1u; + val += (data[pos] as u64) << ((sz * 8u) as u64); + pos += 1u; + } + return val; +} + +// FIXME: #3048 combine trait+impl (or just move these to +// default methods on writer) +/// Generic utility functions defined on writers. +pub trait WriterUtil { + + /// Write a single utf-8 encoded char. + fn write_char(&self, ch: char); + + /// Write every char in the given str, encoded as utf-8. + fn write_str(&self, s: &str); + + /// Write the given str, as utf-8, followed by '\n'. + fn write_line(&self, s: &str); + + /// Write the result of passing n through `int::to_str_bytes`. + fn write_int(&self, n: int); + + /// Write the result of passing n through `uint::to_str_bytes`. + fn write_uint(&self, n: uint); + + /// Write a little-endian uint (number of bytes depends on system). + fn write_le_uint(&self, n: uint); + + /// Write a little-endian int (number of bytes depends on system). + fn write_le_int(&self, n: int); + + /// Write a big-endian uint (number of bytes depends on system). + fn write_be_uint(&self, n: uint); + + /// Write a big-endian int (number of bytes depends on system). + fn write_be_int(&self, n: int); + + /// Write a big-endian u64 (8 bytes). + fn write_be_u64(&self, n: u64); + + /// Write a big-endian u32 (4 bytes). + fn write_be_u32(&self, n: u32); + + /// Write a big-endian u16 (2 bytes). + fn write_be_u16(&self, n: u16); + + /// Write a big-endian i64 (8 bytes). + fn write_be_i64(&self, n: i64); + + /// Write a big-endian i32 (4 bytes). + fn write_be_i32(&self, n: i32); + + /// Write a big-endian i16 (2 bytes). + fn write_be_i16(&self, n: i16); + + /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + fn write_be_f64(&self, f: f64); + + /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + fn write_be_f32(&self, f: f32); + + /// Write a little-endian u64 (8 bytes). + fn write_le_u64(&self, n: u64); + + /// Write a little-endian u32 (4 bytes). + fn write_le_u32(&self, n: u32); + + /// Write a little-endian u16 (2 bytes). + fn write_le_u16(&self, n: u16); + + /// Write a little-endian i64 (8 bytes). + fn write_le_i64(&self, n: i64); + + /// Write a little-endian i32 (4 bytes). + fn write_le_i32(&self, n: i32); + + /// Write a little-endian i16 (2 bytes). + fn write_le_i16(&self, n: i16); + + /// Write a little-endian IEEE754 double-precision floating-point + /// (8 bytes). + fn write_le_f64(&self, f: f64); + + /// Write a litten-endian IEEE754 single-precision floating-point + /// (4 bytes). + fn write_le_f32(&self, f: f32); + + /// Write a u8 (1 byte). + fn write_u8(&self, n: u8); + + /// Write a i8 (1 byte). + fn write_i8(&self, n: i8); +} + +impl<T:Writer> WriterUtil for T { + fn write_char(&self, ch: char) { + if (ch as uint) < 128u { + self.write(&[ch as u8]); + } else { + self.write_str(str::from_char(ch)); + } + } + fn write_str(&self, s: &str) { str::byte_slice(s, |v| self.write(v)) } + fn write_line(&self, s: &str) { + self.write_str(s); + self.write_str(&"\n"); + } + fn write_int(&self, n: int) { + int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + fn write_uint(&self, n: uint) { + uint::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + fn write_le_uint(&self, n: uint) { + u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + fn write_le_int(&self, n: int) { + u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v)) + } + fn write_be_uint(&self, n: uint) { + u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + fn write_be_int(&self, n: int) { + u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v)) + } + fn write_be_u64(&self, n: u64) { + u64_to_be_bytes(n, 8u, |v| self.write(v)) + } + fn write_be_u32(&self, n: u32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + fn write_be_u16(&self, n: u16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + fn write_be_i64(&self, n: i64) { + u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) + } + fn write_be_i32(&self, n: i32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + fn write_be_i16(&self, n: i16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + fn write_be_f64(&self, f:f64) { + unsafe { + self.write_be_u64(cast::transmute(f)) + } + } + fn write_be_f32(&self, f:f32) { + unsafe { + self.write_be_u32(cast::transmute(f)) + } + } + fn write_le_u64(&self, n: u64) { + u64_to_le_bytes(n, 8u, |v| self.write(v)) + } + fn write_le_u32(&self, n: u32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + fn write_le_u16(&self, n: u16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + fn write_le_i64(&self, n: i64) { + u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) + } + fn write_le_i32(&self, n: i32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + fn write_le_i16(&self, n: i16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + fn write_le_f64(&self, f:f64) { + unsafe { + self.write_le_u64(cast::transmute(f)) + } + } + fn write_le_f32(&self, f:f32) { + unsafe { + self.write_le_u32(cast::transmute(f)) + } + } + + fn write_u8(&self, n: u8) { self.write([n]) } + fn write_i8(&self, n: i8) { self.write([n as u8]) } + +} + +#[allow(non_implicitly_copyable_typarams)] +pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result<@Writer, ~str> { + mk_file_writer(path, flags).chain(|w| result::Ok(w)) +} + + +// FIXME: fileflags // #2004 +pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> { + unsafe { + let f = do os::as_c_charp(path.to_str()) |pathbuf| { + do os::as_c_charp("w") |modebuf| { + libc::fopen(pathbuf, modebuf) + } + }; + return if f as uint == 0u { + result::Err(~"error opening " + path.to_str()) + } else { + result::Ok(FILE_writer(f, true)) + } + } +} + +// FIXME (#2004) it would be great if this could be a const +// FIXME (#2004) why are these different from the way stdin() is +// implemented? + + +/** +* Gives a `Writer` which allows you to write to the standard output. +* +* # Examples +* ~~~ +* let stdout = core::io::stdout(); +* stdout.write_str("hello\n"); +* ~~~ +*/ +pub fn stdout() -> @Writer { fd_writer(libc::STDOUT_FILENO as c_int, false) } + +/** +* Gives a `Writer` which allows you to write to standard error. +* +* # Examples +* ~~~ +* let stderr = core::io::stderr(); +* stderr.write_str("hello\n"); +* ~~~ +*/ +pub fn stderr() -> @Writer { fd_writer(libc::STDERR_FILENO as c_int, false) } + +/** +* Prints a string to standard output. +* +* This string will not have an implicit newline at the end. If you want +* an implicit newline, please see `println`. +* +* # Examples +* ~~~ +* // print is imported into the prelude, and so is always available. +* print("hello"); +* ~~~ +*/ +pub fn print(s: &str) { + stdout().write_str(s); +} + +/** +* Prints a string to standard output, followed by a newline. +* +* If you do not want an implicit newline, please see `print`. +* +* # Examples +* ~~~ +* // println is imported into the prelude, and so is always available. +* println("hello"); +* ~~~ +*/ +pub fn println(s: &str) { + stdout().write_line(s); +} + +pub struct BytesWriter { + bytes: @mut ~[u8], + pos: @mut uint, +} + +impl Writer for BytesWriter { + fn write(&self, v: &[u8]) { + let v_len = v.len(); + + let bytes = &mut *self.bytes; + let count = uint::max(bytes.len(), *self.pos + v_len); + vec::reserve(bytes, count); + + unsafe { + // Silly stage0 borrow check workaround... + let casted: &mut ~[u8] = cast::transmute_copy(&bytes); + vec::raw::set_len(casted, count); + + let view = vec::mut_slice(*bytes, *self.pos, count); + vec::bytes::copy_memory(view, v, v_len); + } + + *self.pos += v_len; + } + + fn seek(&self, offset: int, whence: SeekStyle) { + let pos = *self.pos; + let len = vec::uniq_len(&const *self.bytes); + *self.pos = seek_in_buf(offset, pos, len, whence); + } + + fn tell(&self) -> uint { + *self.pos + } + + fn flush(&self) -> int { + 0 + } + + fn get_type(&self) -> WriterType { + File + } +} + +pub fn BytesWriter() -> BytesWriter { + BytesWriter { + bytes: @mut ~[], + pos: @mut 0 + } +} + +pub fn with_bytes_writer(f: &fn(@Writer)) -> ~[u8] { + let wr = @BytesWriter(); + f(wr as @Writer); + let @BytesWriter { bytes, _ } = wr; + copy *bytes +} + +pub fn with_str_writer(f: &fn(@Writer)) -> ~str { + let mut v = with_bytes_writer(f); + + // Make sure the vector has a trailing null and is proper utf8. + v.push(0); + assert!(str::is_utf8(v)); + + unsafe { + ::cast::transmute(v) + } +} + +// Utility functions +pub fn seek_in_buf(offset: int, pos: uint, len: uint, whence: SeekStyle) -> + uint { + let mut bpos = pos as int; + let blen = len as int; + match whence { + SeekSet => bpos = offset, + SeekCur => bpos += offset, + SeekEnd => bpos = blen + offset + } + if bpos < 0 { bpos = 0; } else if bpos > blen { bpos = blen; } + return bpos as uint; +} + +#[allow(non_implicitly_copyable_typarams)] +pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> { + result::chain(read_whole_file(file), |bytes| { + if str::is_utf8(bytes) { + result::Ok(str::from_bytes(bytes)) + } else { + result::Err(file.to_str() + ~" is not UTF-8") + } + }) +} + +// FIXME (#2004): implement this in a low-level way. Going through the +// abstractions is pointless. +#[allow(non_implicitly_copyable_typarams)] +pub fn read_whole_file(file: &Path) -> Result<~[u8], ~str> { + result::chain(file_reader(file), |rdr| { + result::Ok(rdr.read_whole_stream()) + }) +} + +// fsync related + +pub mod fsync { + use io::{FILERes, FdRes, fd_t}; + use kinds::Copy; + use libc; + use ops::Drop; + use option::{None, Option, Some}; + use os; + + pub enum Level { + // whatever fsync does on that platform + FSync, + + // fdatasync on linux, similiar or more on other platforms + FDataSync, + + // full fsync + // + // You must additionally sync the parent directory as well! + FullFSync, + } + + + // Artifacts that need to fsync on destruction + pub struct Res<t> { + arg: Arg<t>, + } + + #[unsafe_destructor] + impl<T:Copy> Drop for Res<T> { + fn finalize(&self) { + match self.arg.opt_level { + None => (), + Some(level) => { + // fail hard if not succesful + assert!(((self.arg.fsync_fn)(self.arg.val, level) + != -1)); + } + } + } + } + + pub fn Res<t: Copy>(arg: Arg<t>) -> Res<t>{ + Res { + arg: arg + } + } + + pub struct Arg<t> { + val: t, + opt_level: Option<Level>, + fsync_fn: @fn(f: t, Level) -> int, + } + + // fsync file after executing blk + // FIXME (#2004) find better way to create resources within lifetime of + // outer res + pub fn FILE_res_sync(file: &FILERes, opt_level: Option<Level>, + blk: &fn(v: Res<*libc::FILE>)) { + blk(Res(Arg { + val: file.f, opt_level: opt_level, + fsync_fn: |file, l| { + unsafe { + os::fsync_fd(libc::fileno(file), l) as int + } + } + })); + } + + // fsync fd after executing blk + pub fn fd_res_sync(fd: &FdRes, opt_level: Option<Level>, + blk: &fn(v: Res<fd_t>)) { + blk(Res(Arg { + val: fd.fd, opt_level: opt_level, + fsync_fn: |fd, l| os::fsync_fd(fd, l) as int + })); + } + + // Type of objects that may want to fsync + pub trait FSyncable { fn fsync(&self, l: Level) -> int; } + + // Call o.fsync after executing blk + pub fn obj_sync(o: @FSyncable, opt_level: Option<Level>, + blk: &fn(v: Res<@FSyncable>)) { + blk(Res(Arg { + val: o, opt_level: opt_level, + fsync_fn: |o, l| o.fsync(l) + })); + } +} + +#[cfg(test)] +mod tests { + use i32; + use io::{BytesWriter, SeekCur, SeekEnd, SeekSet}; + use io; + use path::Path; + use result; + use str; + use u64; + use vec; + + #[test] + fn test_simple() { + let tmpfile = &Path("tmp/lib-io-test-simple.tmp"); + debug!(tmpfile); + let frood: ~str = + ~"A hoopy frood who really knows where his towel is."; + debug!(copy frood); + { + let out: @io::Writer = + result::get( + &io::file_writer(tmpfile, ~[io::Create, io::Truncate])); + out.write_str(frood); + } + let inp: @io::Reader = result::get(&io::file_reader(tmpfile)); + let frood2: ~str = inp.read_c_str(); + debug!(copy frood2); + assert_eq!(frood, frood2); + } + + #[test] + fn test_readchars_empty() { + do io::with_str_reader(~"") |inp| { + let res : ~[char] = inp.read_chars(128); + assert_eq!(res.len(), 0); + } + } + + #[test] + fn test_read_line_utf8() { + do io::with_str_reader(~"生锈的汤匙切肉汤hello生锈的汤匙切肉汤") |inp| { + let line = inp.read_line(); + assert_eq!(line, ~"生锈的汤匙切肉汤hello生锈的汤匙切肉汤"); + } + } + + #[test] + fn test_read_lines() { + do io::with_str_reader(~"a\nb\nc\n") |inp| { + assert_eq!(inp.read_lines(), ~[~"a", ~"b", ~"c"]); + } + + do io::with_str_reader(~"a\nb\nc") |inp| { + assert_eq!(inp.read_lines(), ~[~"a", ~"b", ~"c"]); + } + + do io::with_str_reader(~"") |inp| { + assert!(inp.read_lines().is_empty()); + } + } + + #[test] + fn test_readchars_wide() { + let wide_test = ~"生锈的汤匙切肉汤hello生锈的汤匙切肉汤"; + let ivals : ~[int] = ~[ + 29983, 38152, 30340, 27748, + 21273, 20999, 32905, 27748, + 104, 101, 108, 108, 111, + 29983, 38152, 30340, 27748, + 21273, 20999, 32905, 27748]; + fn check_read_ln(len : uint, s: &str, ivals: &[int]) { + do io::with_str_reader(s) |inp| { + let res : ~[char] = inp.read_chars(len); + if len <= ivals.len() { + assert_eq!(res.len(), len); + } + assert!(vec::slice(ivals, 0u, res.len()) == + vec::map(res, |x| *x as int)); + } + } + let mut i = 0; + while i < 8 { + check_read_ln(i, wide_test, ivals); + i += 1; + } + // check a long read for good measure + check_read_ln(128, wide_test, ivals); + } + + #[test] + fn test_readchar() { + do io::with_str_reader(~"生") |inp| { + let res : char = inp.read_char(); + assert_eq!(res as int, 29983); + } + } + + #[test] + fn test_readchar_empty() { + do io::with_str_reader(~"") |inp| { + let res : char = inp.read_char(); + assert_eq!(res as int, -1); + } + } + + #[test] + fn file_reader_not_exist() { + match io::file_reader(&Path("not a file")) { + result::Err(copy e) => { + assert_eq!(e, ~"error opening not a file"); + } + result::Ok(_) => fail!() + } + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_read_buffer_too_small() { + let path = &Path("tmp/lib-io-test-read-buffer-too-small.tmp"); + // ensure the file exists + io::file_writer(path, [io::Create]).get(); + + let file = io::file_reader(path).get(); + let mut buf = vec::from_elem(5, 0); + file.read(buf, 6); // this should fail because buf is too small + } + + #[test] + fn test_read_buffer_big_enough() { + let path = &Path("tmp/lib-io-test-read-buffer-big-enough.tmp"); + // ensure the file exists + io::file_writer(path, [io::Create]).get(); + + let file = io::file_reader(path).get(); + let mut buf = vec::from_elem(5, 0); + file.read(buf, 4); // this should succeed because buf is big enough + } + + #[test] + fn test_write_empty() { + let file = io::file_writer(&Path("tmp/lib-io-test-write-empty.tmp"), + [io::Create]).get(); + file.write([]); + } + + #[test] + fn file_writer_bad_name() { + match io::file_writer(&Path("?/?"), ~[]) { + result::Err(copy e) => { + assert!(str::starts_with(e, "error opening")); + } + result::Ok(_) => fail!() + } + } + + #[test] + fn buffered_file_writer_bad_name() { + match io::buffered_file_writer(&Path("?/?")) { + result::Err(copy e) => { + assert!(str::starts_with(e, "error opening")); + } + result::Ok(_) => fail!() + } + } + + #[test] + fn bytes_buffer_overwrite() { + let wr = BytesWriter(); + wr.write(~[0u8, 1u8, 2u8, 3u8]); + assert!(*wr.bytes == ~[0u8, 1u8, 2u8, 3u8]); + wr.seek(-2, SeekCur); + wr.write(~[4u8, 5u8, 6u8, 7u8]); + assert!(*wr.bytes == ~[0u8, 1u8, 4u8, 5u8, 6u8, 7u8]); + wr.seek(-2, SeekEnd); + wr.write(~[8u8]); + wr.seek(1, SeekSet); + wr.write(~[9u8]); + assert!(*wr.bytes == ~[0u8, 9u8, 4u8, 5u8, 8u8, 7u8]); + } + + #[test] + fn test_read_write_le() { + let path = Path("tmp/lib-io-test-read-write-le.tmp"); + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + // write the ints to the file + { + let file = io::file_writer(&path, [io::Create]).get(); + for uints.each |i| { + file.write_le_u64(*i); + } + } + + // then read them back and check that they are the same + { + let file = io::file_reader(&path).get(); + for uints.each |i| { + assert_eq!(file.read_le_u64(), *i); + } + } + } + + #[test] + fn test_read_write_be() { + let path = Path("tmp/lib-io-test-read-write-be.tmp"); + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + // write the ints to the file + { + let file = io::file_writer(&path, [io::Create]).get(); + for uints.each |i| { + file.write_be_u64(*i); + } + } + + // then read them back and check that they are the same + { + let file = io::file_reader(&path).get(); + for uints.each |i| { + assert_eq!(file.read_be_u64(), *i); + } + } + } + + #[test] + fn test_read_be_int_n() { + let path = Path("tmp/lib-io-test-read-be-int-n.tmp"); + let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; + + // write the ints to the file + { + let file = io::file_writer(&path, [io::Create]).get(); + for ints.each |i| { + file.write_be_i32(*i); + } + } + + // then read them back and check that they are the same + { + let file = io::file_reader(&path).get(); + for ints.each |i| { + // this tests that the sign extension is working + // (comparing the values as i32 would not test this) + assert_eq!(file.read_be_int_n(4), *i as i64); + } + } + } + + #[test] + fn test_read_f32() { + let path = Path("tmp/lib-io-test-read-f32.tmp"); + //big-endian floating-point 8.1250 + let buf = ~[0x41, 0x02, 0x00, 0x00]; + + { + let file = io::file_writer(&path, [io::Create]).get(); + file.write(buf); + } + + { + let file = io::file_reader(&path).get(); + let f = file.read_be_f32(); + assert_eq!(f, 8.1250); + } + } + +#[test] + fn test_read_write_f32() { + let path = Path("tmp/lib-io-test-read-write-f32.tmp"); + let f:f32 = 8.1250; + + { + let file = io::file_writer(&path, [io::Create]).get(); + file.write_be_f32(f); + file.write_le_f32(f); + } + + { + let file = io::file_reader(&path).get(); + assert_eq!(file.read_be_f32(), 8.1250); + assert_eq!(file.read_le_f32(), 8.1250); + } + } +} diff --git a/src/libstd/io_util.rs b/src/libstd/io_util.rs deleted file mode 100644 index 7d43663cc80..00000000000 --- a/src/libstd/io_util.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 core::io::{Reader, BytesReader}; -use core::io; - -pub struct BufReader { - buf: ~[u8], - pos: @mut uint -} - -pub impl BufReader { - pub fn new(v: ~[u8]) -> BufReader { - BufReader { - buf: v, - pos: @mut 0 - } - } - - priv fn as_bytes_reader<A>(&self, f: &fn(&BytesReader) -> A) -> A { - // Recreating the BytesReader state every call since - // I can't get the borrowing to work correctly - let bytes_reader = BytesReader { - bytes: ::core::util::id::<&[u8]>(self.buf), - pos: @mut *self.pos - }; - - let res = f(&bytes_reader); - - // FIXME #4429: This isn't correct if f fails - *self.pos = *bytes_reader.pos; - - return res; - } -} - -impl Reader for BufReader { - fn read(&self, bytes: &mut [u8], len: uint) -> uint { - self.as_bytes_reader(|r| r.read(bytes, len) ) - } - fn read_byte(&self) -> int { - self.as_bytes_reader(|r| r.read_byte() ) - } - fn eof(&self) -> bool { - self.as_bytes_reader(|r| r.eof() ) - } - fn seek(&self, offset: int, whence: io::SeekStyle) { - self.as_bytes_reader(|r| r.seek(offset, whence) ) - } - fn tell(&self) -> uint { - self.as_bytes_reader(|r| r.tell() ) - } -} diff --git a/src/libstd/iter.rs b/src/libstd/iter.rs new file mode 100644 index 00000000000..57a076bb082 --- /dev/null +++ b/src/libstd/iter.rs @@ -0,0 +1,326 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! Composable internal iterators + +Internal iterators are functions implementing the protocol used by the `for` loop. + +An internal iterator takes `fn(...) -> bool` as a parameter, with returning `false` used to signal +breaking out of iteration. The adaptors in the module work with any such iterator, not just ones +tied to specific traits. For example: + +~~~~ +println(iter::to_vec(|f| uint::range(0, 20, f)).to_str()); +~~~~ + +An external iterator object implementing the interface in the `iterator` module can be used as an +internal iterator by calling the `advance` method. For example: + +~~~~ +use core::iterator::*; + +let xs = [0u, 1, 2, 3, 4, 5]; +let ys = [30, 40, 50, 60]; +let mut it = xs.iter().chain(ys.iter()); +for it.advance |&x: &uint| { + println(x.to_str()); +} +~~~~ + +Internal iterators provide a subset of the functionality of an external iterator. It's not possible +to interleave them to implement algorithms like `zip`, `union` and `merge`. However, they're often +much easier to implement. + +*/ + +use cmp::Ord; +use option::{Option, Some, None}; +use vec::OwnedVector; +use num::{One, Zero}; +use ops::{Add, Mul}; + +pub trait Times { + fn times(&self, it: &fn() -> bool) -> bool; +} + +/** + * Transform an internal iterator into an owned vector. + * + * # Example: + * + * ~~~ + * let xs = ~[1, 2, 3]; + * let ys = do iter::to_vec |f| { xs.each(|x| f(*x)) }; + * assert_eq!(xs, ys); + * ~~~ + */ +#[inline(always)] +pub fn to_vec<T>(iter: &fn(f: &fn(T) -> bool) -> bool) -> ~[T] { + let mut v = ~[]; + for iter |x| { v.push(x) } + v +} + +/** + * Return true if `predicate` is true for any values yielded by an internal iterator. + * + * Example: + * + * ~~~~ + * let xs = ~[1u, 2, 3, 4, 5]; + * assert!(any(|&x: &uint| x > 2, |f| xs.each(f))); + * assert!(!any(|&x: &uint| x > 5, |f| xs.each(f))); + * ~~~~ + */ +#[inline(always)] +pub fn any<T>(predicate: &fn(T) -> bool, + iter: &fn(f: &fn(T) -> bool) -> bool) -> bool { + for iter |x| { + if predicate(x) { + return true; + } + } + return false; +} + +/** + * Return true if `predicate` is true for all values yielded by an internal iterator. + * + * # Example: + * + * ~~~~ + * assert!(all(|&x: &uint| x < 6, |f| uint::range(1, 6, f))); + * assert!(!all(|&x: &uint| x < 5, |f| uint::range(1, 6, f))); + * ~~~~ + */ +#[inline(always)] +pub fn all<T>(predicate: &fn(T) -> bool, + iter: &fn(f: &fn(T) -> bool) -> bool) -> bool { + // If we ever break, iter will return false, so this will only return true + // if predicate returns true for everything. + iter(|x| predicate(x)) +} + +/** + * Return the first element where `predicate` returns `true`. Return `None` if no element is found. + * + * # Example: + * + * ~~~~ + * let xs = ~[1u, 2, 3, 4, 5, 6]; + * assert_eq!(*find(|& &x: & &uint| x > 3, |f| xs.each(f)).unwrap(), 4); + * ~~~~ + */ +#[inline(always)] +pub fn find<T>(predicate: &fn(&T) -> bool, + iter: &fn(f: &fn(T) -> bool) -> bool) -> Option<T> { + for iter |x| { + if predicate(&x) { + return Some(x); + } + } + None +} + +/** + * Return the largest item yielded by an iterator. Return `None` if the iterator is empty. + * + * # Example: + * + * ~~~~ + * let xs = ~[8, 2, 3, 1, -5, 9, 11, 15]; + * assert_eq!(max(|f| xs.each(f)).unwrap(), &15); + * ~~~~ + */ +#[inline] +pub fn max<T: Ord>(iter: &fn(f: &fn(T) -> bool) -> bool) -> Option<T> { + let mut result = None; + for iter |x| { + match result { + Some(ref mut y) => { + if x > *y { + *y = x; + } + } + None => result = Some(x) + } + } + result +} + +/** + * Return the smallest item yielded by an iterator. Return `None` if the iterator is empty. + * + * # Example: + * + * ~~~~ + * let xs = ~[8, 2, 3, 1, -5, 9, 11, 15]; + * assert_eq!(max(|f| xs.each(f)).unwrap(), &-5); + * ~~~~ + */ +#[inline] +pub fn min<T: Ord>(iter: &fn(f: &fn(T) -> bool) -> bool) -> Option<T> { + let mut result = None; + for iter |x| { + match result { + Some(ref mut y) => { + if x < *y { + *y = x; + } + } + None => result = Some(x) + } + } + result +} + +/** + * Reduce an iterator to an accumulated value. + * + * # Example: + * + * ~~~~ + * assert_eq!(fold(0i, |f| int::range(1, 5, f), |a, x| *a += x), 10); + * ~~~~ + */ +#[inline] +pub fn fold<T, U>(start: T, iter: &fn(f: &fn(U) -> bool) -> bool, f: &fn(&mut T, U)) -> T { + let mut result = start; + for iter |x| { + f(&mut result, x); + } + result +} + +/** + * Reduce an iterator to an accumulated value. + * + * `fold_ref` is usable in some generic functions where `fold` is too lenient to type-check, but it + * forces the iterator to yield borrowed pointers. + * + * # Example: + * + * ~~~~ + * fn product<T: One + Mul<T, T>>(iter: &fn(f: &fn(&T) -> bool) -> bool) -> T { + * fold_ref(One::one::<T>(), iter, |a, x| *a = a.mul(x)) + * } + * ~~~~ + */ +#[inline] +pub fn fold_ref<T, U>(start: T, iter: &fn(f: &fn(&U) -> bool) -> bool, f: &fn(&mut T, &U)) -> T { + let mut result = start; + for iter |x| { + f(&mut result, x); + } + result +} + +/** + * Return the sum of the items yielding by an iterator. + * + * # Example: + * + * ~~~~ + * let xs: ~[int] = ~[1, 2, 3, 4]; + * assert_eq!(do sum |f| { xs.each(f) }, 10); + * ~~~~ + */ +#[inline(always)] +pub fn sum<T: Zero + Add<T, T>>(iter: &fn(f: &fn(&T) -> bool) -> bool) -> T { + fold_ref(Zero::zero::<T>(), iter, |a, x| *a = a.add(x)) +} + +/** + * Return the product of the items yielded by an iterator. + * + * # Example: + * + * ~~~~ + * let xs: ~[int] = ~[1, 2, 3, 4]; + * assert_eq!(do product |f| { xs.each(f) }, 24); + * ~~~~ + */ +#[inline(always)] +pub fn product<T: One + Mul<T, T>>(iter: &fn(f: &fn(&T) -> bool) -> bool) -> T { + fold_ref(One::one::<T>(), iter, |a, x| *a = a.mul(x)) +} + +#[cfg(test)] +mod tests { + use super::*; + use prelude::*; + + #[test] + fn test_to_vec() { + let xs = ~[1, 2, 3]; + let ys = do to_vec |f| { xs.each(|x| f(*x)) }; + assert_eq!(xs, ys); + } + + #[test] + fn test_any() { + let xs = ~[1u, 2, 3, 4, 5]; + assert!(any(|&x: &uint| x > 2, |f| xs.each(f))); + assert!(!any(|&x: &uint| x > 5, |f| xs.each(f))); + } + + #[test] + fn test_all() { + assert!(all(|x: uint| x < 6, |f| uint::range(1, 6, f))); + assert!(!all(|x: uint| x < 5, |f| uint::range(1, 6, f))); + } + + #[test] + fn test_find() { + let xs = ~[1u, 2, 3, 4, 5, 6]; + assert_eq!(*find(|& &x: & &uint| x > 3, |f| xs.each(f)).unwrap(), 4); + } + + #[test] + fn test_max() { + let xs = ~[8, 2, 3, 1, -5, 9, 11, 15]; + assert_eq!(max(|f| xs.each(f)).unwrap(), &15); + } + + #[test] + fn test_min() { + let xs = ~[8, 2, 3, 1, -5, 9, 11, 15]; + assert_eq!(min(|f| xs.each(f)).unwrap(), &-5); + } + + #[test] + fn test_fold() { + assert_eq!(fold(0i, |f| int::range(1, 5, f), |a, x| *a += x), 10); + } + + #[test] + fn test_sum() { + let xs: ~[int] = ~[1, 2, 3, 4]; + assert_eq!(do sum |f| { xs.each(f) }, 10); + } + + #[test] + fn test_empty_sum() { + let xs: ~[int] = ~[]; + assert_eq!(do sum |f| { xs.each(f) }, 0); + } + + #[test] + fn test_product() { + let xs: ~[int] = ~[1, 2, 3, 4]; + assert_eq!(do product |f| { xs.each(f) }, 24); + } + + #[test] + fn test_empty_product() { + let xs: ~[int] = ~[]; + assert_eq!(do product |f| { xs.each(f) }, 1); + } +} diff --git a/src/libstd/iterator.rs b/src/libstd/iterator.rs new file mode 100644 index 00000000000..a5679e6dbff --- /dev/null +++ b/src/libstd/iterator.rs @@ -0,0 +1,719 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! Composable external iterators + +The `Iterator` trait defines an interface for objects which implement iteration as a state machine. + +Algorithms like `zip` are provided as `Iterator` implementations which wrap other objects +implementing the `Iterator` trait. + +*/ + +use prelude::*; +use num::{Zero, One}; + +pub trait Iterator<A> { + /// Advance the iterator and return the next value. Return `None` when the end is reached. + fn next(&mut self) -> Option<A>; +} + +/// Iterator adaptors provided for every `Iterator` implementation. The adaptor objects are also +/// implementations of the `Iterator` trait. +/// +/// In the future these will be default methods instead of a utility trait. +pub trait IteratorUtil<A> { + fn chain<U: Iterator<A>>(self, other: U) -> ChainIterator<Self, U>; + fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<Self, U>; + // FIXME: #5898: should be called map + fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, Self>; + fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, Self>; + fn filter_map<'r, B>(self, f: &'r fn(A) -> Option<B>) -> FilterMapIterator<'r, A, B, Self>; + fn enumerate(self) -> EnumerateIterator<Self>; + fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, Self>; + fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, Self>; + fn skip(self, n: uint) -> SkipIterator<Self>; + fn take(self, n: uint) -> TakeIterator<Self>; + fn scan<'r, St, B>(self, initial_state: St, f: &'r fn(&mut St, A) -> Option<B>) + -> ScanIterator<'r, A, B, Self, St>; + fn advance(&mut self, f: &fn(A) -> bool) -> bool; + fn to_vec(&mut self) -> ~[A]; + fn nth(&mut self, n: uint) -> Option<A>; + fn last(&mut self) -> Option<A>; + fn fold<B>(&mut self, start: B, f: &fn(B, A) -> B) -> B; + fn count(&mut self) -> uint; + fn all(&mut self, f: &fn(&A) -> bool) -> bool; + fn any(&mut self, f: &fn(&A) -> bool) -> bool; +} + +/// Iterator adaptors provided for every `Iterator` implementation. The adaptor objects are also +/// implementations of the `Iterator` trait. +/// +/// In the future these will be default methods instead of a utility trait. +impl<A, T: Iterator<A>> IteratorUtil<A> for T { + #[inline(always)] + fn chain<U: Iterator<A>>(self, other: U) -> ChainIterator<T, U> { + ChainIterator{a: self, b: other, flag: false} + } + + #[inline(always)] + fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> { + ZipIterator{a: self, b: other} + } + + // FIXME: #5898: should be called map + #[inline(always)] + fn transform<'r, B>(self, f: &'r fn(A) -> B) -> MapIterator<'r, A, B, T> { + MapIterator{iter: self, f: f} + } + + #[inline(always)] + fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> FilterIterator<'r, A, T> { + FilterIterator{iter: self, predicate: predicate} + } + + #[inline(always)] + fn filter_map<'r, B>(self, f: &'r fn(A) -> Option<B>) -> FilterMapIterator<'r, A, B, T> { + FilterMapIterator { iter: self, f: f } + } + + #[inline(always)] + fn enumerate(self) -> EnumerateIterator<T> { + EnumerateIterator{iter: self, count: 0} + } + + #[inline(always)] + fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhileIterator<'r, A, T> { + SkipWhileIterator{iter: self, flag: false, predicate: predicate} + } + + #[inline(always)] + fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhileIterator<'r, A, T> { + TakeWhileIterator{iter: self, flag: false, predicate: predicate} + } + + #[inline(always)] + fn skip(self, n: uint) -> SkipIterator<T> { + SkipIterator{iter: self, n: n} + } + + #[inline(always)] + fn take(self, n: uint) -> TakeIterator<T> { + TakeIterator{iter: self, n: n} + } + + #[inline(always)] + fn scan<'r, St, B>(self, initial_state: St, f: &'r fn(&mut St, A) -> Option<B>) + -> ScanIterator<'r, A, B, T, St> { + ScanIterator{iter: self, f: f, state: initial_state} + } + + /// A shim implementing the `for` loop iteration protocol for iterator objects + #[inline] + fn advance(&mut self, f: &fn(A) -> bool) -> bool { + loop { + match self.next() { + Some(x) => { + if !f(x) { return false; } + } + None => { return true; } + } + } + } + + #[inline(always)] + fn to_vec(&mut self) -> ~[A] { + iter::to_vec::<A>(|f| self.advance(f)) + } + + /// Return the `n`th item yielded by an iterator. + #[inline(always)] + fn nth(&mut self, mut n: uint) -> Option<A> { + loop { + match self.next() { + Some(x) => if n == 0 { return Some(x) }, + None => return None + } + n -= 1; + } + } + + /// Return the last item yielded by an iterator. + #[inline(always)] + fn last(&mut self) -> Option<A> { + let mut last = None; + for self.advance |x| { last = Some(x); } + last + } + + /// Reduce an iterator to an accumulated value + #[inline] + fn fold<B>(&mut self, init: B, f: &fn(B, A) -> B) -> B { + let mut accum = init; + loop { + match self.next() { + Some(x) => { accum = f(accum, x); } + None => { break; } + } + } + return accum; + } + + /// Count the number of items yielded by an iterator + #[inline(always)] + fn count(&mut self) -> uint { self.fold(0, |cnt, _x| cnt + 1) } + + #[inline(always)] + fn all(&mut self, f: &fn(&A) -> bool) -> bool { + for self.advance |x| { if !f(&x) { return false; } } + return true; + } + + #[inline(always)] + fn any(&mut self, f: &fn(&A) -> bool) -> bool { + for self.advance |x| { if f(&x) { return true; } } + return false; + } +} + +pub trait AdditiveIterator<A> { + fn sum(&mut self) -> A; +} + +impl<A: Add<A, A> + Zero, T: Iterator<A>> AdditiveIterator<A> for T { + #[inline(always)] + fn sum(&mut self) -> A { self.fold(Zero::zero::<A>(), |s, x| s + x) } +} + +pub trait MultiplicativeIterator<A> { + fn product(&mut self) -> A; +} + +impl<A: Mul<A, A> + One, T: Iterator<A>> MultiplicativeIterator<A> for T { + #[inline(always)] + fn product(&mut self) -> A { self.fold(One::one::<A>(), |p, x| p * x) } +} + +pub trait OrdIterator<A> { + fn max(&mut self) -> Option<A>; + fn min(&mut self) -> Option<A>; +} + +impl<A: Ord, T: Iterator<A>> OrdIterator<A> for T { + #[inline(always)] + fn max(&mut self) -> Option<A> { + self.fold(None, |max, x| { + match max { + None => Some(x), + Some(y) => Some(cmp::max(x, y)) + } + }) + } + + #[inline(always)] + fn min(&mut self) -> Option<A> { + self.fold(None, |min, x| { + match min { + None => Some(x), + Some(y) => Some(cmp::min(x, y)) + } + }) + } +} + +pub struct ChainIterator<T, U> { + priv a: T, + priv b: U, + priv flag: bool +} + +impl<A, T: Iterator<A>, U: Iterator<A>> Iterator<A> for ChainIterator<T, U> { + #[inline] + fn next(&mut self) -> Option<A> { + if self.flag { + self.b.next() + } else { + match self.a.next() { + Some(x) => return Some(x), + _ => () + } + self.flag = true; + self.b.next() + } + } +} + +pub struct ZipIterator<T, U> { + priv a: T, + priv b: U +} + +impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<(A, B)> for ZipIterator<T, U> { + #[inline] + fn next(&mut self) -> Option<(A, B)> { + match (self.a.next(), self.b.next()) { + (Some(x), Some(y)) => Some((x, y)), + _ => None + } + } +} + +pub struct MapIterator<'self, A, B, T> { + priv iter: T, + priv f: &'self fn(A) -> B +} + +impl<'self, A, B, T: Iterator<A>> Iterator<B> for MapIterator<'self, A, B, T> { + #[inline] + fn next(&mut self) -> Option<B> { + match self.iter.next() { + Some(a) => Some((self.f)(a)), + _ => None + } + } +} + +pub struct FilterIterator<'self, A, T> { + priv iter: T, + priv predicate: &'self fn(&A) -> bool +} + +impl<'self, A, T: Iterator<A>> Iterator<A> for FilterIterator<'self, A, T> { + #[inline] + fn next(&mut self) -> Option<A> { + for self.iter.advance |x| { + if (self.predicate)(&x) { + return Some(x); + } else { + loop + } + } + None + } +} + +pub struct FilterMapIterator<'self, A, B, T> { + priv iter: T, + priv f: &'self fn(A) -> Option<B> +} + +impl<'self, A, B, T: Iterator<A>> Iterator<B> for FilterMapIterator<'self, A, B, T> { + #[inline] + fn next(&mut self) -> Option<B> { + for self.iter.advance |x| { + match (self.f)(x) { + Some(y) => return Some(y), + None => () + } + } + None + } +} + +pub struct EnumerateIterator<T> { + priv iter: T, + priv count: uint +} + +impl<A, T: Iterator<A>> Iterator<(uint, A)> for EnumerateIterator<T> { + #[inline] + fn next(&mut self) -> Option<(uint, A)> { + match self.iter.next() { + Some(a) => { + let ret = Some((self.count, a)); + self.count += 1; + ret + } + _ => None + } + } +} + +pub struct SkipWhileIterator<'self, A, T> { + priv iter: T, + priv flag: bool, + priv predicate: &'self fn(&A) -> bool +} + +impl<'self, A, T: Iterator<A>> Iterator<A> for SkipWhileIterator<'self, A, T> { + #[inline] + fn next(&mut self) -> Option<A> { + let mut next = self.iter.next(); + if self.flag { + next + } else { + loop { + match next { + Some(x) => { + if (self.predicate)(&x) { + next = self.iter.next(); + loop + } else { + self.flag = true; + return Some(x) + } + } + None => return None + } + } + } + } +} + +pub struct TakeWhileIterator<'self, A, T> { + priv iter: T, + priv flag: bool, + priv predicate: &'self fn(&A) -> bool +} + +impl<'self, A, T: Iterator<A>> Iterator<A> for TakeWhileIterator<'self, A, T> { + #[inline] + fn next(&mut self) -> Option<A> { + if self.flag { + None + } else { + match self.iter.next() { + Some(x) => { + if (self.predicate)(&x) { + Some(x) + } else { + self.flag = true; + None + } + } + None => None + } + } + } +} + +pub struct SkipIterator<T> { + priv iter: T, + priv n: uint +} + +impl<A, T: Iterator<A>> Iterator<A> for SkipIterator<T> { + #[inline] + fn next(&mut self) -> Option<A> { + let mut next = self.iter.next(); + if self.n == 0 { + next + } else { + let n = self.n; + for n.times { + match next { + Some(_) => { + next = self.iter.next(); + loop + } + None => { + self.n = 0; + return None + } + } + } + self.n = 0; + next + } + } +} + +pub struct TakeIterator<T> { + priv iter: T, + priv n: uint +} + +impl<A, T: Iterator<A>> Iterator<A> for TakeIterator<T> { + #[inline] + fn next(&mut self) -> Option<A> { + let next = self.iter.next(); + if self.n != 0 { + self.n -= 1; + next + } else { + None + } + } +} + +pub struct ScanIterator<'self, A, B, T, St> { + priv iter: T, + priv f: &'self fn(&mut St, A) -> Option<B>, + state: St +} + +impl<'self, A, B, T: Iterator<A>, St> Iterator<B> for ScanIterator<'self, A, B, T, St> { + #[inline] + fn next(&mut self) -> Option<B> { + self.iter.next().chain(|a| (self.f)(&mut self.state, a)) + } +} + +pub struct UnfoldrIterator<'self, A, St> { + priv f: &'self fn(&mut St) -> Option<A>, + state: St +} + +pub impl<'self, A, St> UnfoldrIterator<'self, A, St> { + #[inline] + fn new(f: &'self fn(&mut St) -> Option<A>, initial_state: St) + -> UnfoldrIterator<'self, A, St> { + UnfoldrIterator { + f: f, + state: initial_state + } + } +} + +impl<'self, A, St> Iterator<A> for UnfoldrIterator<'self, A, St> { + #[inline] + fn next(&mut self) -> Option<A> { + (self.f)(&mut self.state) + } +} + +/// An infinite iterator starting at `start` and advancing by `step` with each iteration +pub struct Counter<A> { + state: A, + step: A +} + +pub impl<A> Counter<A> { + #[inline(always)] + fn new(start: A, step: A) -> Counter<A> { + Counter{state: start, step: step} + } +} + +impl<A: Add<A, A> + Clone> Iterator<A> for Counter<A> { + #[inline(always)] + fn next(&mut self) -> Option<A> { + let result = self.state.clone(); + self.state = self.state.add(&self.step); // FIXME: #6050 + Some(result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use prelude::*; + + #[test] + fn test_counter_to_vec() { + let mut it = Counter::new(0, 5).take(10); + let xs = iter::to_vec(|f| it.advance(f)); + assert_eq!(xs, ~[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); + } + + #[test] + fn test_iterator_chain() { + let xs = [0u, 1, 2, 3, 4, 5]; + let ys = [30u, 40, 50, 60]; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + let mut it = xs.iter().chain(ys.iter()); + let mut i = 0; + for it.advance |&x: &uint| { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); + + let ys = Counter::new(30u, 10).take(4); + let mut it = xs.iter().transform(|&x| x).chain(ys); + let mut i = 0; + for it.advance |x: uint| { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); + } + + #[test] + fn test_filter_map() { + let mut it = Counter::new(0u, 1u).take(10) + .filter_map(|x: uint| if x.is_even() { Some(x*x) } else { None }); + assert_eq!(it.to_vec(), ~[0*0, 2*2, 4*4, 6*6, 8*8]); + } + + #[test] + fn test_iterator_enumerate() { + let xs = [0u, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + for it.advance |(i, &x): (uint, &uint)| { + assert_eq!(i, x); + } + } + + #[test] + fn test_iterator_take_while() { + let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0u, 1, 2, 3, 5, 13]; + let mut it = xs.iter().take_while(|&x| *x < 15u); + let mut i = 0; + for it.advance |&x: &uint| { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); + } + + #[test] + fn test_iterator_skip_while() { + let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let mut it = xs.iter().skip_while(|&x| *x < 15u); + let mut i = 0; + for it.advance |&x: &uint| { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); + } + + #[test] + fn test_iterator_skip() { + let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + let mut it = xs.iter().skip(5); + let mut i = 0; + for it.advance |&x: &uint| { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); + } + + #[test] + fn test_iterator_take() { + let xs = [0u, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0u, 1, 2, 3, 5]; + let mut it = xs.iter().take(5); + let mut i = 0; + for it.advance |&x: &uint| { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); + } + + #[test] + fn test_iterator_scan() { + // test the type inference + fn add(old: &mut int, new: &uint) -> Option<float> { + *old += *new as int; + Some(*old as float) + } + let xs = [0u, 1, 2, 3, 4]; + let ys = [0f, 1f, 3f, 6f, 10f]; + + let mut it = xs.iter().scan(0, add); + let mut i = 0; + for it.advance |x| { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); + } + + #[test] + fn test_unfoldr() { + fn count(st: &mut uint) -> Option<uint> { + if *st < 10 { + let ret = Some(*st); + *st += 1; + ret + } else { + None + } + } + + let mut it = UnfoldrIterator::new(count, 0); + let mut i = 0; + for it.advance |counted| { + assert_eq!(counted, i); + i += 1; + } + assert_eq!(i, 10); + } + + #[test] + fn test_iterator_nth() { + let v = &[0, 1, 2, 3, 4]; + for uint::range(0, v.len()) |i| { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + } + + #[test] + fn test_iterator_last() { + let v = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v.slice(0, 1).iter().last().unwrap(), &0); + } + + #[test] + fn test_iterator_count() { + let v = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v.slice(0, 4).iter().count(), 4); + assert_eq!(v.slice(0, 10).iter().count(), 10); + assert_eq!(v.slice(0, 0).iter().count(), 0); + } + + #[test] + fn test_iterator_sum() { + let v = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v.slice(0, 4).iter().transform(|&x| x).sum(), 6); + assert_eq!(v.iter().transform(|&x| x).sum(), 55); + assert_eq!(v.slice(0, 0).iter().transform(|&x| x).sum(), 0); + } + + #[test] + fn test_iterator_product() { + let v = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v.slice(0, 4).iter().transform(|&x| x).product(), 0); + assert_eq!(v.slice(1, 5).iter().transform(|&x| x).product(), 24); + assert_eq!(v.slice(0, 0).iter().transform(|&x| x).product(), 1); + } + + #[test] + fn test_iterator_max() { + let v = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v.slice(0, 4).iter().transform(|&x| x).max(), Some(3)); + assert_eq!(v.iter().transform(|&x| x).max(), Some(10)); + assert_eq!(v.slice(0, 0).iter().transform(|&x| x).max(), None); + } + + #[test] + fn test_iterator_min() { + let v = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v.slice(0, 4).iter().transform(|&x| x).min(), Some(0)); + assert_eq!(v.iter().transform(|&x| x).min(), Some(0)); + assert_eq!(v.slice(0, 0).iter().transform(|&x| x).min(), None); + } + + #[test] + fn test_all() { + let v = ~&[1, 2, 3, 4, 5]; + assert!(v.iter().all(|&x| *x < 10)); + assert!(!v.iter().all(|&x| x.is_even())); + assert!(!v.iter().all(|&x| *x > 100)); + assert!(v.slice(0, 0).iter().all(|_| fail!())); + } + + #[test] + fn test_any() { + let v = ~&[1, 2, 3, 4, 5]; + assert!(v.iter().any(|&x| *x < 10)); + assert!(v.iter().any(|&x| x.is_even())); + assert!(!v.iter().any(|&x| *x > 100)); + assert!(!v.slice(0, 0).iter().any(|_| fail!())); + } +} diff --git a/src/libstd/json.rs b/src/libstd/json.rs deleted file mode 100644 index 44e965b5c43..00000000000 --- a/src/libstd/json.rs +++ /dev/null @@ -1,1958 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Rust JSON serialization library -// Copyright (c) 2011 Google Inc. -#[forbid(non_camel_case_types)]; - -//! json serialization - -use core::io::{WriterUtil, ReaderUtil}; -use core::hashmap::HashMap; - -use serialize::Encodable; -use serialize; -use sort::Sort; - -/// Represents a json value -pub enum Json { - Number(float), - String(~str), - Boolean(bool), - List(List), - Object(~Object), - Null, -} - -pub type List = ~[Json]; -pub type Object = HashMap<~str, Json>; - -#[deriving(Eq)] -pub struct Error { - line: uint, - col: uint, - msg: @~str, -} - -fn escape_str(s: &str) -> ~str { - let mut escaped = ~"\""; - for str::each_char(s) |c| { - match c { - '"' => escaped += ~"\\\"", - '\\' => escaped += ~"\\\\", - '\x08' => escaped += ~"\\b", - '\x0c' => escaped += ~"\\f", - '\n' => escaped += ~"\\n", - '\r' => escaped += ~"\\r", - '\t' => escaped += ~"\\t", - _ => escaped += str::from_char(c) - } - }; - - escaped += ~"\""; - - escaped -} - -fn spaces(n: uint) -> ~str { - let mut ss = ~""; - for n.times { str::push_str(&mut ss, " "); } - return ss; -} - -pub struct Encoder { - priv wr: @io::Writer, -} - -pub fn Encoder(wr: @io::Writer) -> Encoder { - Encoder { - wr: wr - } -} - -impl serialize::Encoder for Encoder { - fn emit_nil(&mut self) { self.wr.write_str("null") } - - fn emit_uint(&mut self, v: uint) { self.emit_float(v as float); } - fn emit_u64(&mut self, v: u64) { self.emit_float(v as float); } - fn emit_u32(&mut self, v: u32) { self.emit_float(v as float); } - fn emit_u16(&mut self, v: u16) { self.emit_float(v as float); } - fn emit_u8(&mut self, v: u8) { self.emit_float(v as float); } - - fn emit_int(&mut self, v: int) { self.emit_float(v as float); } - fn emit_i64(&mut self, v: i64) { self.emit_float(v as float); } - fn emit_i32(&mut self, v: i32) { self.emit_float(v as float); } - fn emit_i16(&mut self, v: i16) { self.emit_float(v as float); } - fn emit_i8(&mut self, v: i8) { self.emit_float(v as float); } - - fn emit_bool(&mut self, v: bool) { - if v { - self.wr.write_str("true"); - } else { - self.wr.write_str("false"); - } - } - - fn emit_f64(&mut self, v: f64) { self.emit_float(v as float); } - fn emit_f32(&mut self, v: f32) { self.emit_float(v as float); } - fn emit_float(&mut self, v: float) { - self.wr.write_str(float::to_str_digits(v, 6u)); - } - - fn emit_char(&mut self, v: char) { self.emit_str(str::from_char(v)) } - fn emit_str(&mut self, v: &str) { self.wr.write_str(escape_str(v)) } - - fn emit_enum(&mut self, _name: &str, f: &fn(&mut Encoder)) { f(self) } - - fn emit_enum_variant(&mut self, - name: &str, - _id: uint, - cnt: uint, - f: &fn(&mut Encoder)) { - // enums are encoded as strings or vectors: - // Bunny => "Bunny" - // Kangaroo(34,"William") => ["Kangaroo",[34,"William"]] - - if cnt == 0 { - self.wr.write_str(escape_str(name)); - } else { - self.wr.write_char('['); - self.wr.write_str(escape_str(name)); - self.wr.write_char(','); - f(self); - self.wr.write_char(']'); - } - } - - fn emit_enum_variant_arg(&mut self, idx: uint, f: &fn(&mut Encoder)) { - if idx != 0 { - self.wr.write_char(','); - } - f(self); - } - - fn emit_enum_struct_variant(&mut self, - name: &str, - id: uint, - cnt: uint, - f: &fn(&mut Encoder)) { - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, - _: &str, - idx: uint, - f: &fn(&mut Encoder)) { - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, _: uint, f: &fn(&mut Encoder)) { - self.wr.write_char('{'); - f(self); - self.wr.write_char('}'); - } - - fn emit_struct_field(&mut self, - name: &str, - idx: uint, - f: &fn(&mut Encoder)) { - if idx != 0 { self.wr.write_char(','); } - self.wr.write_str(escape_str(name)); - self.wr.write_char(':'); - f(self); - } - - fn emit_tuple(&mut self, len: uint, f: &fn(&mut Encoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: uint, f: &fn(&mut Encoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, - _name: &str, - len: uint, - f: &fn(&mut Encoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: uint, f: &fn(&mut Encoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: &fn(&mut Encoder)) { f(self); } - fn emit_option_none(&mut self) { self.emit_nil(); } - fn emit_option_some(&mut self, f: &fn(&mut Encoder)) { f(self); } - - fn emit_seq(&mut self, _len: uint, f: &fn(&mut Encoder)) { - self.wr.write_char('['); - f(self); - self.wr.write_char(']'); - } - - fn emit_seq_elt(&mut self, idx: uint, f: &fn(&mut Encoder)) { - if idx != 0 { - self.wr.write_char(','); - } - f(self) - } - - fn emit_map(&mut self, _len: uint, f: &fn(&mut Encoder)) { - self.wr.write_char('{'); - f(self); - self.wr.write_char('}'); - } - - fn emit_map_elt_key(&mut self, idx: uint, f: &fn(&mut Encoder)) { - if idx != 0 { self.wr.write_char(','); } - f(self) - } - - fn emit_map_elt_val(&mut self, _idx: uint, f: &fn(&mut Encoder)) { - self.wr.write_char(':'); - f(self) - } -} - -pub struct PrettyEncoder { - priv wr: @io::Writer, - priv indent: uint, -} - -pub fn PrettyEncoder(wr: @io::Writer) -> PrettyEncoder { - PrettyEncoder { - wr: wr, - indent: 0, - } -} - -impl serialize::Encoder for PrettyEncoder { - fn emit_nil(&mut self) { self.wr.write_str("null") } - - fn emit_uint(&mut self, v: uint) { self.emit_float(v as float); } - fn emit_u64(&mut self, v: u64) { self.emit_float(v as float); } - fn emit_u32(&mut self, v: u32) { self.emit_float(v as float); } - fn emit_u16(&mut self, v: u16) { self.emit_float(v as float); } - fn emit_u8(&mut self, v: u8) { self.emit_float(v as float); } - - fn emit_int(&mut self, v: int) { self.emit_float(v as float); } - fn emit_i64(&mut self, v: i64) { self.emit_float(v as float); } - fn emit_i32(&mut self, v: i32) { self.emit_float(v as float); } - fn emit_i16(&mut self, v: i16) { self.emit_float(v as float); } - fn emit_i8(&mut self, v: i8) { self.emit_float(v as float); } - - fn emit_bool(&mut self, v: bool) { - if v { - self.wr.write_str("true"); - } else { - self.wr.write_str("false"); - } - } - - fn emit_f64(&mut self, v: f64) { self.emit_float(v as float); } - fn emit_f32(&mut self, v: f32) { self.emit_float(v as float); } - fn emit_float(&mut self, v: float) { - self.wr.write_str(float::to_str_digits(v, 6u)); - } - - fn emit_char(&mut self, v: char) { self.emit_str(str::from_char(v)) } - fn emit_str(&mut self, v: &str) { self.wr.write_str(escape_str(v)); } - - fn emit_enum(&mut self, _name: &str, f: &fn(&mut PrettyEncoder)) { - f(self) - } - - fn emit_enum_variant(&mut self, - name: &str, - _: uint, - cnt: uint, - f: &fn(&mut PrettyEncoder)) { - if cnt == 0 { - self.wr.write_str(escape_str(name)); - } else { - self.wr.write_char('['); - self.indent += 2; - self.wr.write_char('\n'); - self.wr.write_str(spaces(self.indent)); - self.wr.write_str(escape_str(name)); - self.wr.write_str(",\n"); - f(self); - self.wr.write_char('\n'); - self.indent -= 2; - self.wr.write_str(spaces(self.indent)); - self.wr.write_char(']'); - } - } - - fn emit_enum_variant_arg(&mut self, - idx: uint, - f: &fn(&mut PrettyEncoder)) { - if idx != 0 { - self.wr.write_str(",\n"); - } - self.wr.write_str(spaces(self.indent)); - f(self) - } - - fn emit_enum_struct_variant(&mut self, - name: &str, - id: uint, - cnt: uint, - f: &fn(&mut PrettyEncoder)) { - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, - _: &str, - idx: uint, - f: &fn(&mut PrettyEncoder)) { - self.emit_enum_variant_arg(idx, f) - } - - - fn emit_struct(&mut self, - _: &str, - len: uint, - f: &fn(&mut PrettyEncoder)) { - if len == 0 { - self.wr.write_str("{}"); - } else { - self.wr.write_char('{'); - self.indent += 2; - f(self); - self.wr.write_char('\n'); - self.indent -= 2; - self.wr.write_str(spaces(self.indent)); - self.wr.write_char('}'); - } - } - - fn emit_struct_field(&mut self, - name: &str, - idx: uint, - f: &fn(&mut PrettyEncoder)) { - if idx == 0 { - self.wr.write_char('\n'); - } else { - self.wr.write_str(",\n"); - } - self.wr.write_str(spaces(self.indent)); - self.wr.write_str(escape_str(name)); - self.wr.write_str(": "); - f(self); - } - - fn emit_tuple(&mut self, len: uint, f: &fn(&mut PrettyEncoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: uint, f: &fn(&mut PrettyEncoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, - _: &str, - len: uint, - f: &fn(&mut PrettyEncoder)) { - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, - idx: uint, - f: &fn(&mut PrettyEncoder)) { - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: &fn(&mut PrettyEncoder)) { f(self); } - fn emit_option_none(&mut self) { self.emit_nil(); } - fn emit_option_some(&mut self, f: &fn(&mut PrettyEncoder)) { f(self); } - - fn emit_seq(&mut self, len: uint, f: &fn(&mut PrettyEncoder)) { - if len == 0 { - self.wr.write_str("[]"); - } else { - self.wr.write_char('['); - self.indent += 2; - f(self); - self.wr.write_char('\n'); - self.indent -= 2; - self.wr.write_str(spaces(self.indent)); - self.wr.write_char(']'); - } - } - - fn emit_seq_elt(&mut self, idx: uint, f: &fn(&mut PrettyEncoder)) { - if idx == 0 { - self.wr.write_char('\n'); - } else { - self.wr.write_str(",\n"); - } - self.wr.write_str(spaces(self.indent)); - f(self) - } - - fn emit_map(&mut self, len: uint, f: &fn(&mut PrettyEncoder)) { - if len == 0 { - self.wr.write_str("{}"); - } else { - self.wr.write_char('{'); - self.indent += 2; - f(self); - self.wr.write_char('\n'); - self.indent -= 2; - self.wr.write_str(spaces(self.indent)); - self.wr.write_char('}'); - } - } - - fn emit_map_elt_key(&mut self, idx: uint, f: &fn(&mut PrettyEncoder)) { - if idx == 0 { - self.wr.write_char('\n'); - } else { - self.wr.write_str(",\n"); - } - self.wr.write_str(spaces(self.indent)); - f(self); - } - - fn emit_map_elt_val(&mut self, _idx: uint, f: &fn(&mut PrettyEncoder)) { - self.wr.write_str(": "); - f(self); - } -} - -impl<E: serialize::Encoder> serialize::Encodable<E> for Json { - fn encode(&self, e: &mut E) { - match *self { - Number(v) => v.encode(e), - String(ref v) => v.encode(e), - Boolean(v) => v.encode(e), - List(ref v) => v.encode(e), - Object(ref v) => v.encode(e), - Null => e.emit_nil(), - } - } -} - -/// Encodes a json value into a io::writer -pub fn to_writer(wr: @io::Writer, json: &Json) { - let mut encoder = Encoder(wr); - json.encode(&mut encoder) -} - -/// Encodes a json value into a string -pub fn to_str(json: &Json) -> ~str { - io::with_str_writer(|wr| to_writer(wr, json)) -} - -/// Encodes a json value into a io::writer -pub fn to_pretty_writer(wr: @io::Writer, json: &Json) { - let mut encoder = PrettyEncoder(wr); - json.encode(&mut encoder) -} - -/// Encodes a json value into a string -pub fn to_pretty_str(json: &Json) -> ~str { - io::with_str_writer(|wr| to_pretty_writer(wr, json)) -} - -pub struct Parser { - priv rdr: @io::Reader, - priv ch: char, - priv line: uint, - priv col: uint, -} - -/// Decode a json value from an io::reader -pub fn Parser(rdr: @io::Reader) -> Parser { - Parser { - rdr: rdr, - ch: rdr.read_char(), - line: 1, - col: 1, - } -} - -pub impl Parser { - fn parse(&mut self) -> Result<Json, Error> { - match self.parse_value() { - Ok(value) => { - // Skip trailing whitespaces. - self.parse_whitespace(); - // Make sure there is no trailing characters. - if self.eof() { - Ok(value) - } else { - self.error(~"trailing characters") - } - } - Err(e) => Err(e) - } - } -} - -priv impl Parser { - fn eof(&self) -> bool { self.ch == -1 as char } - - fn bump(&mut self) { - self.ch = self.rdr.read_char(); - - if self.ch == '\n' { - self.line += 1u; - self.col = 1u; - } else { - self.col += 1u; - } - } - - fn next_char(&mut self) -> char { - self.bump(); - self.ch - } - - fn error<T>(&self, msg: ~str) -> Result<T, Error> { - Err(Error { line: self.line, col: self.col, msg: @msg }) - } - - fn parse_value(&mut self) -> Result<Json, Error> { - self.parse_whitespace(); - - if self.eof() { return self.error(~"EOF while parsing value"); } - - match self.ch { - 'n' => self.parse_ident("ull", Null), - 't' => self.parse_ident("rue", Boolean(true)), - 'f' => self.parse_ident("alse", Boolean(false)), - '0' .. '9' | '-' => self.parse_number(), - '"' => - match self.parse_str() { - Ok(s) => Ok(String(s)), - Err(e) => Err(e), - }, - '[' => self.parse_list(), - '{' => self.parse_object(), - _ => self.error(~"invalid syntax") - } - } - - fn parse_whitespace(&mut self) { - while char::is_whitespace(self.ch) { self.bump(); } - } - - fn parse_ident(&mut self, ident: &str, value: Json) -> Result<Json, Error> { - if str::all(ident, |c| c == self.next_char()) { - self.bump(); - Ok(value) - } else { - self.error(~"invalid syntax") - } - } - - fn parse_number(&mut self) -> Result<Json, Error> { - let mut neg = 1f; - - if self.ch == '-' { - self.bump(); - neg = -1f; - } - - let mut res = match self.parse_integer() { - Ok(res) => res, - Err(e) => return Err(e) - }; - - if self.ch == '.' { - match self.parse_decimal(res) { - Ok(r) => res = r, - Err(e) => return Err(e) - } - } - - if self.ch == 'e' || self.ch == 'E' { - match self.parse_exponent(res) { - Ok(r) => res = r, - Err(e) => return Err(e) - } - } - - Ok(Number(neg * res)) - } - - fn parse_integer(&mut self) -> Result<float, Error> { - let mut res = 0f; - - match self.ch { - '0' => { - self.bump(); - - // There can be only one leading '0'. - match self.ch { - '0' .. '9' => return self.error(~"invalid number"), - _ => () - } - } - '1' .. '9' => { - while !self.eof() { - match self.ch { - '0' .. '9' => { - res *= 10f; - res += ((self.ch as int) - ('0' as int)) as float; - - self.bump(); - } - _ => break - } - } - } - _ => return self.error(~"invalid number") - } - - Ok(res) - } - - fn parse_decimal(&mut self, res: float) -> Result<float, Error> { - self.bump(); - - // Make sure a digit follows the decimal place. - match self.ch { - '0' .. '9' => (), - _ => return self.error(~"invalid number") - } - - let mut res = res; - let mut dec = 1f; - while !self.eof() { - match self.ch { - '0' .. '9' => { - dec /= 10f; - res += (((self.ch as int) - ('0' as int)) as float) * dec; - - self.bump(); - } - _ => break - } - } - - Ok(res) - } - - fn parse_exponent(&mut self, mut res: float) -> Result<float, Error> { - self.bump(); - - let mut exp = 0u; - let mut neg_exp = false; - - match self.ch { - '+' => self.bump(), - '-' => { self.bump(); neg_exp = true; } - _ => () - } - - // Make sure a digit follows the exponent place. - match self.ch { - '0' .. '9' => (), - _ => return self.error(~"invalid number") - } - - while !self.eof() { - match self.ch { - '0' .. '9' => { - exp *= 10u; - exp += (self.ch as uint) - ('0' as uint); - - self.bump(); - } - _ => break - } - } - - let exp = float::pow_with_uint(10u, exp); - if neg_exp { - res /= exp; - } else { - res *= exp; - } - - Ok(res) - } - - fn parse_str(&mut self) -> Result<~str, Error> { - let mut escape = false; - let mut res = ~""; - - while !self.eof() { - self.bump(); - - if (escape) { - match self.ch { - '"' => str::push_char(&mut res, '"'), - '\\' => str::push_char(&mut res, '\\'), - '/' => str::push_char(&mut res, '/'), - 'b' => str::push_char(&mut res, '\x08'), - 'f' => str::push_char(&mut res, '\x0c'), - 'n' => str::push_char(&mut res, '\n'), - 'r' => str::push_char(&mut res, '\r'), - 't' => str::push_char(&mut res, '\t'), - 'u' => { - // Parse \u1234. - let mut i = 0u; - let mut n = 0u; - while i < 4u { - match self.next_char() { - '0' .. '9' => { - n = n * 16u + (self.ch as uint) - - ('0' as uint); - }, - 'a' | 'A' => n = n * 16u + 10u, - 'b' | 'B' => n = n * 16u + 11u, - 'c' | 'C' => n = n * 16u + 12u, - 'd' | 'D' => n = n * 16u + 13u, - 'e' | 'E' => n = n * 16u + 14u, - 'f' | 'F' => n = n * 16u + 15u, - _ => return self.error( - ~"invalid \\u escape (unrecognized hex)") - } - i += 1u; - } - - // Error out if we didn't parse 4 digits. - if i != 4u { - return self.error( - ~"invalid \\u escape (not four digits)"); - } - - str::push_char(&mut res, n as char); - } - _ => return self.error(~"invalid escape") - } - escape = false; - } else if self.ch == '\\' { - escape = true; - } else { - if self.ch == '"' { - self.bump(); - return Ok(res); - } - str::push_char(&mut res, self.ch); - } - } - - self.error(~"EOF while parsing string") - } - - fn parse_list(&mut self) -> Result<Json, Error> { - self.bump(); - self.parse_whitespace(); - - let mut values = ~[]; - - if self.ch == ']' { - self.bump(); - return Ok(List(values)); - } - - loop { - match self.parse_value() { - Ok(v) => values.push(v), - Err(e) => return Err(e) - } - - self.parse_whitespace(); - if self.eof() { - return self.error(~"EOF while parsing list"); - } - - match self.ch { - ',' => self.bump(), - ']' => { self.bump(); return Ok(List(values)); } - _ => return self.error(~"expected `,` or `]`") - } - }; - } - - fn parse_object(&mut self) -> Result<Json, Error> { - self.bump(); - self.parse_whitespace(); - - let mut values = ~HashMap::new(); - - if self.ch == '}' { - self.bump(); - return Ok(Object(values)); - } - - while !self.eof() { - self.parse_whitespace(); - - if self.ch != '"' { - return self.error(~"key must be a string"); - } - - let key = match self.parse_str() { - Ok(key) => key, - Err(e) => return Err(e) - }; - - self.parse_whitespace(); - - if self.ch != ':' { - if self.eof() { break; } - return self.error(~"expected `:`"); - } - self.bump(); - - match self.parse_value() { - Ok(value) => { values.insert(key, value); } - Err(e) => return Err(e) - } - self.parse_whitespace(); - - match self.ch { - ',' => self.bump(), - '}' => { self.bump(); return Ok(Object(values)); } - _ => { - if self.eof() { break; } - return self.error(~"expected `,` or `}`"); - } - } - } - - return self.error(~"EOF while parsing object"); - } -} - -/// Decodes a json value from an @io::Reader -pub fn from_reader(rdr: @io::Reader) -> Result<Json, Error> { - let mut parser = Parser(rdr); - parser.parse() -} - -/// Decodes a json value from a string -pub fn from_str(s: &str) -> Result<Json, Error> { - do io::with_str_reader(s) |rdr| { - from_reader(rdr) - } -} - -pub struct Decoder { - priv stack: ~[Json], -} - -pub fn Decoder(json: Json) -> Decoder { - Decoder { - stack: ~[json] - } -} - -impl serialize::Decoder for Decoder { - fn read_nil(&mut self) -> () { - debug!("read_nil"); - match self.stack.pop() { - Null => (), - value => fail!("not a null: %?", value) - } - } - - fn read_u64(&mut self) -> u64 { self.read_float() as u64 } - fn read_u32(&mut self) -> u32 { self.read_float() as u32 } - fn read_u16(&mut self) -> u16 { self.read_float() as u16 } - fn read_u8 (&mut self) -> u8 { self.read_float() as u8 } - fn read_uint(&mut self) -> uint { self.read_float() as uint } - - fn read_i64(&mut self) -> i64 { self.read_float() as i64 } - fn read_i32(&mut self) -> i32 { self.read_float() as i32 } - fn read_i16(&mut self) -> i16 { self.read_float() as i16 } - fn read_i8 (&mut self) -> i8 { self.read_float() as i8 } - fn read_int(&mut self) -> int { self.read_float() as int } - - fn read_bool(&mut self) -> bool { - debug!("read_bool"); - match self.stack.pop() { - Boolean(b) => b, - value => fail!("not a boolean: %?", value) - } - } - - fn read_f64(&mut self) -> f64 { self.read_float() as f64 } - fn read_f32(&mut self) -> f32 { self.read_float() as f32 } - fn read_float(&mut self) -> float { - debug!("read_float"); - match self.stack.pop() { - Number(f) => f, - value => fail!("not a number: %?", value) - } - } - - fn read_char(&mut self) -> char { - let mut v = ~[]; - for str::each_char(self.read_str()) |c| { v.push(c) } - if v.len() != 1 { fail!("string must have one character") } - v[0] - } - - fn read_str(&mut self) -> ~str { - debug!("read_str"); - match self.stack.pop() { - String(s) => s, - json => fail!("not a string: %?", json) - } - } - - fn read_enum<T>(&mut self, name: &str, f: &fn(&mut Decoder) -> T) -> T { - debug!("read_enum(%s)", name); - f(self) - } - - fn read_enum_variant<T>(&mut self, - names: &[&str], - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_enum_variant(names=%?)", names); - let name = match self.stack.pop() { - String(s) => s, - List(list) => { - do vec::consume_reverse(list) |_i, v| { - self.stack.push(v); - } - match self.stack.pop() { - String(s) => s, - value => fail!("invalid variant name: %?", value), - } - } - ref json => fail!("invalid variant: %?", *json), - }; - let idx = match vec::position(names, |n| str::eq_slice(*n, name)) { - Some(idx) => idx, - None => fail!("Unknown variant name: %?", name), - }; - f(self, idx) - } - - fn read_enum_variant_arg<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_enum_variant_arg(idx=%u)", idx); - f(self) - } - - fn read_enum_struct_variant<T>(&mut self, - names: &[&str], - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_enum_struct_variant(names=%?)", names); - self.read_enum_variant(names, f) - } - - - fn read_enum_struct_variant_field<T>(&mut self, - name: &str, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_enum_struct_variant_field(name=%?, idx=%u)", name, idx); - self.read_enum_variant_arg(idx, f) - } - - fn read_struct<T>(&mut self, - name: &str, - len: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_struct(name=%s, len=%u)", name, len); - let value = f(self); - self.stack.pop(); - value - } - - fn read_struct_field<T>(&mut self, - name: &str, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_struct_field(name=%?, idx=%u)", name, idx); - match self.stack.pop() { - Object(obj) => { - let mut obj = obj; - let value = match obj.pop(&name.to_owned()) { - None => fail!("no such field: %s", name), - Some(json) => { - self.stack.push(json); - f(self) - } - }; - self.stack.push(Object(obj)); - value - } - value => fail!("not an object: %?", value) - } - } - - fn read_tuple<T>(&mut self, f: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_tuple()"); - self.read_seq(f) - } - - fn read_tuple_arg<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_tuple_arg(idx=%u)", idx); - self.read_seq_elt(idx, f) - } - - fn read_tuple_struct<T>(&mut self, - name: &str, - f: &fn(&mut Decoder, uint) -> T) - -> T { - debug!("read_tuple_struct(name=%?)", name); - self.read_tuple(f) - } - - fn read_tuple_struct_arg<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_tuple_struct_arg(idx=%u)", idx); - self.read_tuple_arg(idx, f) - } - - fn read_option<T>(&mut self, f: &fn(&mut Decoder, bool) -> T) -> T { - match self.stack.pop() { - Null => f(self, false), - value => { self.stack.push(value); f(self, true) } - } - } - - fn read_seq<T>(&mut self, f: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_seq()"); - let len = match self.stack.pop() { - List(list) => { - let len = list.len(); - do vec::consume_reverse(list) |_i, v| { - self.stack.push(v); - } - len - } - _ => fail!("not a list"), - }; - f(self, len) - } - - fn read_seq_elt<T>(&mut self, idx: uint, f: &fn(&mut Decoder) -> T) -> T { - debug!("read_seq_elt(idx=%u)", idx); - f(self) - } - - fn read_map<T>(&mut self, f: &fn(&mut Decoder, uint) -> T) -> T { - debug!("read_map()"); - let len = match self.stack.pop() { - Object(obj) => { - let mut obj = obj; - let len = obj.len(); - do obj.consume |key, value| { - self.stack.push(value); - self.stack.push(String(key)); - } - len - } - json => fail!("not an object: %?", json), - }; - f(self, len) - } - - fn read_map_elt_key<T>(&mut self, - idx: uint, - f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_map_elt_key(idx=%u)", idx); - f(self) - } - - fn read_map_elt_val<T>(&mut self, idx: uint, f: &fn(&mut Decoder) -> T) - -> T { - debug!("read_map_elt_val(idx=%u)", idx); - f(self) - } -} - -impl Eq for Json { - fn eq(&self, other: &Json) -> bool { - match (self) { - &Number(f0) => - match other { &Number(f1) => f0 == f1, _ => false }, - &String(ref s0) => - match other { &String(ref s1) => s0 == s1, _ => false }, - &Boolean(b0) => - match other { &Boolean(b1) => b0 == b1, _ => false }, - &Null => - match other { &Null => true, _ => false }, - &List(ref v0) => - match other { &List(ref v1) => v0 == v1, _ => false }, - &Object(ref d0) => { - match other { - &Object(ref d1) => { - if d0.len() == d1.len() { - let mut equal = true; - for d0.each |k, v0| { - match d1.find(k) { - Some(v1) if v0 == v1 => { }, - _ => { equal = false; break } - } - }; - equal - } else { - false - } - } - _ => false - } - } - } - } - fn ne(&self, other: &Json) -> bool { !self.eq(other) } -} - -/// Test if two json values are less than one another -impl Ord for Json { - fn lt(&self, other: &Json) -> bool { - match (*self) { - Number(f0) => { - match *other { - Number(f1) => f0 < f1, - String(_) | Boolean(_) | List(_) | Object(_) | - Null => true - } - } - - String(ref s0) => { - match *other { - Number(_) => false, - String(ref s1) => s0 < s1, - Boolean(_) | List(_) | Object(_) | Null => true - } - } - - Boolean(b0) => { - match *other { - Number(_) | String(_) => false, - Boolean(b1) => b0 < b1, - List(_) | Object(_) | Null => true - } - } - - List(ref l0) => { - match *other { - Number(_) | String(_) | Boolean(_) => false, - List(ref l1) => (*l0) < (*l1), - Object(_) | Null => true - } - } - - Object(ref d0) => { - match *other { - Number(_) | String(_) | Boolean(_) | List(_) => false, - Object(ref d1) => { - let mut d0_flat = ~[]; - let mut d1_flat = ~[]; - - // FIXME #4430: this is horribly inefficient... - for d0.each |k, v| { - d0_flat.push((@copy *k, @copy *v)); - } - d0_flat.qsort(); - - for d1.each |k, v| { - d1_flat.push((@copy *k, @copy *v)); - } - d1_flat.qsort(); - - d0_flat < d1_flat - } - Null => true - } - } - - Null => { - match *other { - Number(_) | String(_) | Boolean(_) | List(_) | - Object(_) => - false, - Null => true - } - } - } - } - fn le(&self, other: &Json) -> bool { !(*other).lt(&(*self)) } - fn ge(&self, other: &Json) -> bool { !(*self).lt(other) } - fn gt(&self, other: &Json) -> bool { (*other).lt(&(*self)) } -} - -trait ToJson { fn to_json(&self) -> Json; } - -impl ToJson for Json { - fn to_json(&self) -> Json { copy *self } -} - -impl ToJson for @Json { - fn to_json(&self) -> Json { (**self).to_json() } -} - -impl ToJson for int { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for i8 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for i16 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for i32 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for i64 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for uint { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for u8 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for u16 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for u32 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for u64 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for float { - fn to_json(&self) -> Json { Number(*self) } -} - -impl ToJson for f32 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for f64 { - fn to_json(&self) -> Json { Number(*self as float) } -} - -impl ToJson for () { - fn to_json(&self) -> Json { Null } -} - -impl ToJson for bool { - fn to_json(&self) -> Json { Boolean(*self) } -} - -impl ToJson for ~str { - fn to_json(&self) -> Json { String(copy *self) } -} - -impl ToJson for @~str { - fn to_json(&self) -> Json { String(copy **self) } -} - -impl<A:ToJson,B:ToJson> ToJson for (A, B) { - fn to_json(&self) -> Json { - match *self { - (ref a, ref b) => { - List(~[a.to_json(), b.to_json()]) - } - } - } -} - -impl<A:ToJson,B:ToJson,C:ToJson> ToJson for (A, B, C) { - fn to_json(&self) -> Json { - match *self { - (ref a, ref b, ref c) => { - List(~[a.to_json(), b.to_json(), c.to_json()]) - } - } - } -} - -impl<A:ToJson> ToJson for ~[A] { - fn to_json(&self) -> Json { List(self.map(|elt| elt.to_json())) } -} - -impl<A:ToJson + Copy> ToJson for HashMap<~str, A> { - fn to_json(&self) -> Json { - let mut d = HashMap::new(); - for self.each |key, value| { - d.insert(copy *key, value.to_json()); - } - Object(~d) - } -} - -impl<A:ToJson> ToJson for Option<A> { - fn to_json(&self) -> Json { - match *self { - None => Null, - Some(ref value) => value.to_json() - } - } -} - -impl to_str::ToStr for Json { - fn to_str(&self) -> ~str { to_str(self) } -} - -impl to_str::ToStr for Error { - fn to_str(&self) -> ~str { - fmt!("%u:%u: %s", self.line, self.col, *self.msg) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use core::hashmap::HashMap; - - use std::serialize::Decodable; - - #[deriving(Eq, Encodable, Decodable)] - enum Animal { - Dog, - Frog(~str, int) - } - - #[deriving(Eq, Encodable, Decodable)] - struct Inner { - a: (), - b: uint, - c: ~[~str], - } - - #[deriving(Eq, Encodable, Decodable)] - struct Outer { - inner: ~[Inner], - } - - fn mk_object(items: &[(~str, Json)]) -> Json { - let mut d = ~HashMap::new(); - - for items.each |item| { - match *item { - (copy key, copy value) => { d.insert(key, value); }, - } - }; - - Object(d) - } - - #[test] - fn test_write_null() { - assert_eq!(to_str(&Null), ~"null"); - assert_eq!(to_pretty_str(&Null), ~"null"); - } - - - #[test] - fn test_write_number() { - assert_eq!(to_str(&Number(3f)), ~"3"); - assert_eq!(to_pretty_str(&Number(3f)), ~"3"); - - assert_eq!(to_str(&Number(3.1f)), ~"3.1"); - assert_eq!(to_pretty_str(&Number(3.1f)), ~"3.1"); - - assert_eq!(to_str(&Number(-1.5f)), ~"-1.5"); - assert_eq!(to_pretty_str(&Number(-1.5f)), ~"-1.5"); - - assert_eq!(to_str(&Number(0.5f)), ~"0.5"); - assert_eq!(to_pretty_str(&Number(0.5f)), ~"0.5"); - } - - #[test] - fn test_write_str() { - assert_eq!(to_str(&String(~"")), ~"\"\""); - assert_eq!(to_pretty_str(&String(~"")), ~"\"\""); - - assert_eq!(to_str(&String(~"foo")), ~"\"foo\""); - assert_eq!(to_pretty_str(&String(~"foo")), ~"\"foo\""); - } - - #[test] - fn test_write_bool() { - assert_eq!(to_str(&Boolean(true)), ~"true"); - assert_eq!(to_pretty_str(&Boolean(true)), ~"true"); - - assert_eq!(to_str(&Boolean(false)), ~"false"); - assert_eq!(to_pretty_str(&Boolean(false)), ~"false"); - } - - #[test] - fn test_write_list() { - assert_eq!(to_str(&List(~[])), ~"[]"); - assert_eq!(to_pretty_str(&List(~[])), ~"[]"); - - assert_eq!(to_str(&List(~[Boolean(true)])), ~"[true]"); - assert_eq!( - to_pretty_str(&List(~[Boolean(true)])), - ~"\ - [\n \ - true\n\ - ]" - ); - - assert_eq!(to_str(&List(~[ - Boolean(false), - Null, - List(~[String(~"foo\nbar"), Number(3.5f)]) - ])), ~"[false,null,[\"foo\\nbar\",3.5]]"); - assert_eq!( - to_pretty_str(&List(~[ - Boolean(false), - Null, - List(~[String(~"foo\nbar"), Number(3.5f)]) - ])), - ~"\ - [\n \ - false,\n \ - null,\n \ - [\n \ - \"foo\\nbar\",\n \ - 3.5\n \ - ]\n\ - ]" - ); - } - - #[test] - fn test_write_object() { - assert_eq!(to_str(&mk_object(~[])), ~"{}"); - assert_eq!(to_pretty_str(&mk_object(~[])), ~"{}"); - - assert_eq!( - to_str(&mk_object(~[(~"a", Boolean(true))])), - ~"{\"a\":true}" - ); - assert_eq!( - to_pretty_str(&mk_object(~[(~"a", Boolean(true))])), - ~"\ - {\n \ - \"a\": true\n\ - }" - ); - - assert_eq!( - to_str(&mk_object(~[ - (~"b", List(~[ - mk_object(~[(~"c", String(~"\x0c\r"))]), - mk_object(~[(~"d", String(~""))]) - ])) - ])), - ~"{\ - \"b\":[\ - {\"c\":\"\\f\\r\"},\ - {\"d\":\"\"}\ - ]\ - }" - ); - assert_eq!( - to_pretty_str(&mk_object(~[ - (~"b", List(~[ - mk_object(~[(~"c", String(~"\x0c\r"))]), - mk_object(~[(~"d", String(~""))]) - ])) - ])), - ~"\ - {\n \ - \"b\": [\n \ - {\n \ - \"c\": \"\\f\\r\"\n \ - },\n \ - {\n \ - \"d\": \"\"\n \ - }\n \ - ]\n\ - }" - ); - - let a = mk_object(~[ - (~"a", Boolean(true)), - (~"b", List(~[ - mk_object(~[(~"c", String(~"\x0c\r"))]), - mk_object(~[(~"d", String(~""))]) - ])) - ]); - - // We can't compare the strings directly because the object fields be - // printed in a different order. - assert_eq!(copy a, from_str(to_str(&a)).unwrap()); - assert_eq!(copy a, from_str(to_pretty_str(&a)).unwrap()); - } - - #[test] - fn test_write_enum() { - let animal = Dog; - assert_eq!( - do io::with_str_writer |wr| { - let mut encoder = Encoder(wr); - animal.encode(&mut encoder); - }, - ~"\"Dog\"" - ); - assert_eq!( - do io::with_str_writer |wr| { - let mut encoder = PrettyEncoder(wr); - animal.encode(&mut encoder); - }, - ~"\"Dog\"" - ); - - let animal = Frog(~"Henry", 349); - assert_eq!( - do io::with_str_writer |wr| { - let mut encoder = Encoder(wr); - animal.encode(&mut encoder); - }, - ~"[\"Frog\",\"Henry\",349]" - ); - assert_eq!( - do io::with_str_writer |wr| { - let mut encoder = PrettyEncoder(wr); - animal.encode(&mut encoder); - }, - ~"\ - [\n \ - \"Frog\",\n \ - \"Henry\",\n \ - 349\n\ - ]" - ); - } - - #[test] - fn test_write_some() { - let value = Some(~"jodhpurs"); - let s = do io::with_str_writer |wr| { - let mut encoder = Encoder(wr); - value.encode(&mut encoder); - }; - assert_eq!(s, ~"\"jodhpurs\""); - - let value = Some(~"jodhpurs"); - let s = do io::with_str_writer |wr| { - let mut encoder = PrettyEncoder(wr); - value.encode(&mut encoder); - }; - assert_eq!(s, ~"\"jodhpurs\""); - } - - #[test] - fn test_write_none() { - let value: Option<~str> = None; - let s = do io::with_str_writer |wr| { - let mut encoder = Encoder(wr); - value.encode(&mut encoder); - }; - assert_eq!(s, ~"null"); - - let s = do io::with_str_writer |wr| { - let mut encoder = Encoder(wr); - value.encode(&mut encoder); - }; - assert_eq!(s, ~"null"); - } - - #[test] - fn test_trailing_characters() { - assert_eq!(from_str(~"nulla"), - Err(Error {line: 1u, col: 5u, msg: @~"trailing characters"})); - assert_eq!(from_str(~"truea"), - Err(Error {line: 1u, col: 5u, msg: @~"trailing characters"})); - assert_eq!(from_str(~"falsea"), - Err(Error {line: 1u, col: 6u, msg: @~"trailing characters"})); - assert_eq!(from_str(~"1a"), - Err(Error {line: 1u, col: 2u, msg: @~"trailing characters"})); - assert_eq!(from_str(~"[]a"), - Err(Error {line: 1u, col: 3u, msg: @~"trailing characters"})); - assert_eq!(from_str(~"{}a"), - Err(Error {line: 1u, col: 3u, msg: @~"trailing characters"})); - } - - #[test] - fn test_read_identifiers() { - assert_eq!(from_str(~"n"), - Err(Error {line: 1u, col: 2u, msg: @~"invalid syntax"})); - assert_eq!(from_str(~"nul"), - Err(Error {line: 1u, col: 4u, msg: @~"invalid syntax"})); - - assert_eq!(from_str(~"t"), - Err(Error {line: 1u, col: 2u, msg: @~"invalid syntax"})); - assert_eq!(from_str(~"truz"), - Err(Error {line: 1u, col: 4u, msg: @~"invalid syntax"})); - - assert_eq!(from_str(~"f"), - Err(Error {line: 1u, col: 2u, msg: @~"invalid syntax"})); - assert_eq!(from_str(~"faz"), - Err(Error {line: 1u, col: 3u, msg: @~"invalid syntax"})); - - assert_eq!(from_str(~"null"), Ok(Null)); - assert_eq!(from_str(~"true"), Ok(Boolean(true))); - assert_eq!(from_str(~"false"), Ok(Boolean(false))); - assert_eq!(from_str(~" null "), Ok(Null)); - assert_eq!(from_str(~" true "), Ok(Boolean(true))); - assert_eq!(from_str(~" false "), Ok(Boolean(false))); - } - - #[test] - fn test_decode_identifiers() { - let mut decoder = Decoder(from_str(~"null").unwrap()); - let v: () = Decodable::decode(&mut decoder); - assert_eq!(v, ()); - - let mut decoder = Decoder(from_str(~"true").unwrap()); - let v: bool = Decodable::decode(&mut decoder); - assert_eq!(v, true); - - let mut decoder = Decoder(from_str(~"false").unwrap()); - let v: bool = Decodable::decode(&mut decoder); - assert_eq!(v, false); - } - - #[test] - fn test_read_number() { - assert_eq!(from_str(~"+"), - Err(Error {line: 1u, col: 1u, msg: @~"invalid syntax"})); - assert_eq!(from_str(~"."), - Err(Error {line: 1u, col: 1u, msg: @~"invalid syntax"})); - - assert_eq!(from_str(~"-"), - Err(Error {line: 1u, col: 2u, msg: @~"invalid number"})); - assert_eq!(from_str(~"00"), - Err(Error {line: 1u, col: 2u, msg: @~"invalid number"})); - assert_eq!(from_str(~"1."), - Err(Error {line: 1u, col: 3u, msg: @~"invalid number"})); - assert_eq!(from_str(~"1e"), - Err(Error {line: 1u, col: 3u, msg: @~"invalid number"})); - assert_eq!(from_str(~"1e+"), - Err(Error {line: 1u, col: 4u, msg: @~"invalid number"})); - - assert_eq!(from_str(~"3"), Ok(Number(3f))); - assert_eq!(from_str(~"3.1"), Ok(Number(3.1f))); - assert_eq!(from_str(~"-1.2"), Ok(Number(-1.2f))); - assert_eq!(from_str(~"0.4"), Ok(Number(0.4f))); - assert_eq!(from_str(~"0.4e5"), Ok(Number(0.4e5f))); - assert_eq!(from_str(~"0.4e+15"), Ok(Number(0.4e15f))); - assert_eq!(from_str(~"0.4e-01"), Ok(Number(0.4e-01f))); - assert_eq!(from_str(~" 3 "), Ok(Number(3f))); - } - - #[test] - fn test_decode_numbers() { - let mut decoder = Decoder(from_str(~"3").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 3f); - - let mut decoder = Decoder(from_str(~"3.1").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 3.1f); - - let mut decoder = Decoder(from_str(~"-1.2").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, -1.2f); - - let mut decoder = Decoder(from_str(~"0.4").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 0.4f); - - let mut decoder = Decoder(from_str(~"0.4e5").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 0.4e5f); - - let mut decoder = Decoder(from_str(~"0.4e15").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 0.4e15f); - - let mut decoder = Decoder(from_str(~"0.4e-01").unwrap()); - let v: float = Decodable::decode(&mut decoder); - assert_eq!(v, 0.4e-01f); - } - - #[test] - fn test_read_str() { - assert_eq!(from_str(~"\""), - Err(Error {line: 1u, col: 2u, msg: @~"EOF while parsing string" - })); - assert_eq!(from_str(~"\"lol"), - Err(Error {line: 1u, col: 5u, msg: @~"EOF while parsing string" - })); - - assert_eq!(from_str(~"\"\""), Ok(String(~""))); - assert_eq!(from_str(~"\"foo\""), Ok(String(~"foo"))); - assert_eq!(from_str(~"\"\\\"\""), Ok(String(~"\""))); - assert_eq!(from_str(~"\"\\b\""), Ok(String(~"\x08"))); - assert_eq!(from_str(~"\"\\n\""), Ok(String(~"\n"))); - assert_eq!(from_str(~"\"\\r\""), Ok(String(~"\r"))); - assert_eq!(from_str(~"\"\\t\""), Ok(String(~"\t"))); - assert_eq!(from_str(~" \"foo\" "), Ok(String(~"foo"))); - assert_eq!(from_str(~"\"\\u12ab\""), Ok(String(~"\u12ab"))); - assert_eq!(from_str(~"\"\\uAB12\""), Ok(String(~"\uAB12"))); - } - - #[test] - fn test_decode_str() { - let mut decoder = Decoder(from_str(~"\"\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~""); - - let mut decoder = Decoder(from_str(~"\"foo\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"foo"); - - let mut decoder = Decoder(from_str(~"\"\\\"\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\""); - - let mut decoder = Decoder(from_str(~"\"\\b\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\x08"); - - let mut decoder = Decoder(from_str(~"\"\\n\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\n"); - - let mut decoder = Decoder(from_str(~"\"\\r\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\r"); - - let mut decoder = Decoder(from_str(~"\"\\t\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\t"); - - let mut decoder = Decoder(from_str(~"\"\\u12ab\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\u12ab"); - - let mut decoder = Decoder(from_str(~"\"\\uAB12\"").unwrap()); - let v: ~str = Decodable::decode(&mut decoder); - assert_eq!(v, ~"\uAB12"); - } - - #[test] - fn test_read_list() { - assert_eq!(from_str(~"["), - Err(Error {line: 1u, col: 2u, msg: @~"EOF while parsing value"})); - assert_eq!(from_str(~"[1"), - Err(Error {line: 1u, col: 3u, msg: @~"EOF while parsing list"})); - assert_eq!(from_str(~"[1,"), - Err(Error {line: 1u, col: 4u, msg: @~"EOF while parsing value"})); - assert_eq!(from_str(~"[1,]"), - Err(Error {line: 1u, col: 4u, msg: @~"invalid syntax"})); - assert_eq!(from_str(~"[6 7]"), - Err(Error {line: 1u, col: 4u, msg: @~"expected `,` or `]`"})); - - assert_eq!(from_str(~"[]"), Ok(List(~[]))); - assert_eq!(from_str(~"[ ]"), Ok(List(~[]))); - assert_eq!(from_str(~"[true]"), Ok(List(~[Boolean(true)]))); - assert_eq!(from_str(~"[ false ]"), Ok(List(~[Boolean(false)]))); - assert_eq!(from_str(~"[null]"), Ok(List(~[Null]))); - assert_eq!(from_str(~"[3, 1]"), - Ok(List(~[Number(3f), Number(1f)]))); - assert_eq!(from_str(~"\n[3, 2]\n"), - Ok(List(~[Number(3f), Number(2f)]))); - assert_eq!(from_str(~"[2, [4, 1]]"), - Ok(List(~[Number(2f), List(~[Number(4f), Number(1f)])]))); - } - - #[test] - fn test_decode_list() { - let mut decoder = Decoder(from_str(~"[]").unwrap()); - let v: ~[()] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[]); - - let mut decoder = Decoder(from_str(~"[null]").unwrap()); - let v: ~[()] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[()]); - - let mut decoder = Decoder(from_str(~"[true]").unwrap()); - let v: ~[bool] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[true]); - - let mut decoder = Decoder(from_str(~"[true]").unwrap()); - let v: ~[bool] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[true]); - - let mut decoder = Decoder(from_str(~"[3, 1]").unwrap()); - let v: ~[int] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[3, 1]); - - let mut decoder = Decoder(from_str(~"[[3], [1, 2]]").unwrap()); - let v: ~[~[uint]] = Decodable::decode(&mut decoder); - assert_eq!(v, ~[~[3], ~[1, 2]]); - } - - #[test] - fn test_read_object() { - assert_eq!(from_str(~"{"), - Err(Error { - line: 1u, - col: 2u, - msg: @~"EOF while parsing object"})); - assert_eq!(from_str(~"{ "), - Err(Error { - line: 1u, - col: 3u, - msg: @~"EOF while parsing object"})); - assert_eq!(from_str(~"{1"), - Err(Error { - line: 1u, - col: 2u, - msg: @~"key must be a string"})); - assert_eq!(from_str(~"{ \"a\""), - Err(Error { - line: 1u, - col: 6u, - msg: @~"EOF while parsing object"})); - assert_eq!(from_str(~"{\"a\""), - Err(Error { - line: 1u, - col: 5u, - msg: @~"EOF while parsing object"})); - assert_eq!(from_str(~"{\"a\" "), - Err(Error { - line: 1u, - col: 6u, - msg: @~"EOF while parsing object"})); - - assert_eq!(from_str(~"{\"a\" 1"), - Err(Error {line: 1u, col: 6u, msg: @~"expected `:`"})); - assert_eq!(from_str(~"{\"a\":"), - Err(Error {line: 1u, col: 6u, msg: @~"EOF while parsing value"})); - assert_eq!(from_str(~"{\"a\":1"), - Err(Error { - line: 1u, - col: 7u, - msg: @~"EOF while parsing object"})); - assert_eq!(from_str(~"{\"a\":1 1"), - Err(Error {line: 1u, col: 8u, msg: @~"expected `,` or `}`"})); - assert_eq!(from_str(~"{\"a\":1,"), - Err(Error { - line: 1u, - col: 8u, - msg: @~"EOF while parsing object"})); - - assert_eq!(result::unwrap(from_str(~"{}")), mk_object(~[])); - assert_eq!(result::unwrap(from_str(~"{\"a\": 3}")), - mk_object(~[(~"a", Number(3.0f))])); - - assert_eq!(result::unwrap(from_str( - ~"{ \"a\": null, \"b\" : true }")), - mk_object(~[ - (~"a", Null), - (~"b", Boolean(true))])); - assert_eq!(result::unwrap( - from_str(~"\n{ \"a\": null, \"b\" : true }\n")), - mk_object(~[ - (~"a", Null), - (~"b", Boolean(true))])); - assert_eq!(result::unwrap(from_str( - ~"{\"a\" : 1.0 ,\"b\": [ true ]}")), - mk_object(~[ - (~"a", Number(1.0)), - (~"b", List(~[Boolean(true)])) - ])); - assert_eq!(result::unwrap(from_str( - ~"{" + - ~"\"a\": 1.0, " + - ~"\"b\": [" + - ~"true," + - ~"\"foo\\nbar\", " + - ~"{ \"c\": {\"d\": null} } " + - ~"]" + - ~"}")), - mk_object(~[ - (~"a", Number(1.0f)), - (~"b", List(~[ - Boolean(true), - String(~"foo\nbar"), - mk_object(~[ - (~"c", mk_object(~[(~"d", Null)])) - ]) - ])) - ])); - } - - #[test] - fn test_decode_struct() { - let s = ~"{ - \"inner\": [ - { \"a\": null, \"b\": 2, \"c\": [\"abc\", \"xyz\"] } - ] - }"; - let mut decoder = Decoder(from_str(s).unwrap()); - let v: Outer = Decodable::decode(&mut decoder); - assert_eq!( - v, - Outer { - inner: ~[ - Inner { a: (), b: 2, c: ~[~"abc", ~"xyz"] } - ] - } - ); - } - - #[test] - fn test_decode_option() { - let mut decoder = Decoder(from_str(~"null").unwrap()); - let value: Option<~str> = Decodable::decode(&mut decoder); - assert_eq!(value, None); - - let mut decoder = Decoder(from_str(~"\"jodhpurs\"").unwrap()); - let value: Option<~str> = Decodable::decode(&mut decoder); - assert_eq!(value, Some(~"jodhpurs")); - } - - #[test] - fn test_decode_enum() { - let mut decoder = Decoder(from_str(~"\"Dog\"").unwrap()); - let value: Animal = Decodable::decode(&mut decoder); - assert_eq!(value, Dog); - - let mut decoder = - Decoder(from_str(~"[\"Frog\",\"Henry\",349]").unwrap()); - let value: Animal = Decodable::decode(&mut decoder); - assert_eq!(value, Frog(~"Henry", 349)); - } - - #[test] - fn test_decode_map() { - let s = ~"{\"a\": \"Dog\", \"b\": [\"Frog\", \"Henry\", 349]}"; - let mut decoder = Decoder(from_str(s).unwrap()); - let mut map: HashMap<~str, Animal> = Decodable::decode(&mut decoder); - - assert_eq!(map.pop(&~"a"), Some(Dog)); - assert_eq!(map.pop(&~"b"), Some(Frog(~"Henry", 349))); - } - - #[test] - fn test_multiline_errors() { - assert_eq!(from_str(~"{\n \"foo\":\n \"bar\""), - Err(Error { - line: 3u, - col: 8u, - msg: @~"EOF while parsing object"})); - } -} diff --git a/src/libstd/kinds.rs b/src/libstd/kinds.rs new file mode 100644 index 00000000000..d9b3e35b6b9 --- /dev/null +++ b/src/libstd/kinds.rs @@ -0,0 +1,53 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +The kind traits + +Rust types can be classified in various useful ways according to +intrinsic properties of the type. These classifications, often called +'kinds', are represented as traits. + +They cannot be implemented by user code, but are instead implemented +by the compiler automatically for the types to which they apply. + +The 4 kinds are + +* Copy - types that may be copied without allocation. This includes + scalar types and managed pointers, and exludes owned pointers. It + also excludes types that implement `Drop`. + +* Owned - owned types and types containing owned types. These types + may be transferred across task boundaries. + +* Const - types that are deeply immutable. Const types are used for + freezable data structures. + +`Copy` types include both implicitly copyable types that the compiler +will copy automatically and non-implicitly copyable types that require +the `copy` keyword to copy. Types that do not implement `Copy` may +instead implement `Clone`. + +*/ + +#[lang="copy"] +pub trait Copy { + // Empty. +} + +#[lang="owned"] +pub trait Owned { + // Empty. +} + +#[lang="const"] +pub trait Const { + // Empty. +} diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs new file mode 100644 index 00000000000..7ae3f0fd2d4 --- /dev/null +++ b/src/libstd/libc.rs @@ -0,0 +1,2054 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +* Bindings for the C standard library and other platform libraries +* +* This module contains bindings to the C standard library, +* organized into modules by their defining standard. +* Additionally, it contains some assorted platform-specific definitions. +* For convenience, most functions and types are reexported from `core::libc`, +* so `pub use core::libc::*` will import the available +* C bindings as appropriate for the target platform. The exact +* set of functions available are platform specific. +* +* *Note* Rustdoc does not indicate reexports currently. Also, because these +* definitions are platform-specific, some may not +* appear in the generated documentation. +* +* We consider the following specs reasonably normative with respect +* to interoperating with the C standard library (libc/msvcrt): +* +* * ISO 9899:1990 ('C95', 'ANSI C', 'Standard C'), NA1, 1995. +* * ISO 9899:1999 ('C99' or 'C9x'). +* * ISO 9945:1988 / IEEE 1003.1-1988 ('POSIX.1'). +* * ISO 9945:2001 / IEEE 1003.1-2001 ('POSIX:2001', 'SUSv3'). +* * ISO 9945:2008 / IEEE 1003.1-2008 ('POSIX:2008', 'SUSv4'). +* +* Note that any reference to the 1996 revision of POSIX, or any revs +* between 1990 (when '88 was approved at ISO) and 2001 (when the next +* actual revision-revision happened), are merely additions of other +* chapters (1b and 1c) outside the core interfaces. +* +* Despite having several names each, these are *reasonably* coherent +* point-in-time, list-of-definition sorts of specs. You can get each under a +* variety of names but will wind up with the same definition in each case. +* +* See standards(7) in linux-manpages for more details. +* +* Our interface to these libraries is complicated by the non-universality of +* conformance to any of them. About the only thing universally supported is +* the first (C95), beyond that definitions quickly become absent on various +* platforms. +* +* We therefore wind up dividing our module-space up (mostly for the sake of +* sanity while editing, filling-in-details and eliminating duplication) into +* definitions common-to-all (held in modules named c95, c99, posix88, posix01 +* and posix08) and definitions that appear only on *some* platforms (named +* 'extra'). This would be things like significant OSX foundation kit, or +* win32 library kernel32.dll, or various fancy glibc, linux or BSD +* extensions. +* +* In addition to the per-platform 'extra' modules, we define a module of +* 'common BSD' libc routines that never quite made it into POSIX but show up +* in multiple derived systems. This is the 4.4BSD r2 / 1995 release, the +* final one from Berkeley after the lawsuits died down and the CSRG +* dissolved. +*/ + +#[allow(non_camel_case_types)]; + +// Initial glob-exports mean that all the contents of all the modules +// wind up exported, if you're interested in writing platform-specific code. + +pub use libc::types::common::c95::*; +pub use libc::types::common::c99::*; +pub use libc::types::common::posix88::*; +pub use libc::types::common::posix01::*; +pub use libc::types::common::posix08::*; +pub use libc::types::common::bsd44::*; +pub use libc::types::os::common::posix01::*; +pub use libc::types::os::arch::c95::*; +pub use libc::types::os::arch::c99::*; +pub use libc::types::os::arch::posix88::*; +pub use libc::types::os::arch::posix01::*; +pub use libc::types::os::arch::posix08::*; +pub use libc::types::os::arch::bsd44::*; +pub use libc::types::os::arch::extra::*; + +pub use libc::consts::os::c95::*; +pub use libc::consts::os::c99::*; +pub use libc::consts::os::posix88::*; +pub use libc::consts::os::posix01::*; +pub use libc::consts::os::posix08::*; +pub use libc::consts::os::bsd44::*; +pub use libc::consts::os::extra::*; + +pub use libc::funcs::c95::ctype::*; +pub use libc::funcs::c95::stdio::*; +pub use libc::funcs::c95::stdlib::*; +pub use libc::funcs::c95::string::*; + +pub use libc::funcs::posix88::stat_::*; +pub use libc::funcs::posix88::stdio::*; +pub use libc::funcs::posix88::fcntl::*; +pub use libc::funcs::posix88::dirent::*; +pub use libc::funcs::posix88::unistd::*; + +pub use libc::funcs::posix01::stat_::*; +pub use libc::funcs::posix01::unistd::*; +pub use libc::funcs::posix01::glob::*; +pub use libc::funcs::posix08::unistd::*; + +pub use libc::funcs::bsd44::*; +pub use libc::funcs::extra::*; + +#[cfg(target_os = "win32")] +pub use libc::funcs::extra::kernel32::*; +#[cfg(target_os = "win32")] +pub use libc::funcs::extra::msvcrt::*; + +// Explicit export lists for the intersection (provided here) mean that +// you can write more-platform-agnostic code if you stick to just these +// symbols. + +pub use libc::types::common::c95::{FILE, c_void, fpos_t}; +pub use libc::types::common::posix88::{DIR, dirent_t}; +pub use libc::types::os::arch::c95::{c_char, c_double, c_float, c_int}; +pub use libc::types::os::arch::c95::{c_long, c_short, c_uchar, c_ulong}; +pub use libc::types::os::arch::c95::{c_ushort, clock_t, ptrdiff_t}; +pub use libc::types::os::arch::c95::{size_t, time_t}; +pub use libc::types::os::arch::c99::{c_longlong, c_ulonglong, intptr_t}; +pub use libc::types::os::arch::c99::{uintptr_t}; +pub use libc::types::os::arch::posix88::{dev_t, dirent_t, ino_t, mode_t}; +pub use libc::types::os::arch::posix88::{off_t, pid_t, ssize_t}; + +pub use libc::consts::os::c95::{_IOFBF, _IOLBF, _IONBF, BUFSIZ, EOF}; +pub use libc::consts::os::c95::{EXIT_FAILURE, EXIT_SUCCESS}; +pub use libc::consts::os::c95::{FILENAME_MAX, FOPEN_MAX, L_tmpnam}; +pub use libc::consts::os::c95::{RAND_MAX, SEEK_CUR, SEEK_END}; +pub use libc::consts::os::c95::{SEEK_SET, TMP_MAX}; +pub use libc::consts::os::posix88::{F_OK, O_APPEND, O_CREAT, O_EXCL}; +pub use libc::consts::os::posix88::{O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +pub use libc::consts::os::posix88::{R_OK, S_IEXEC, S_IFBLK, S_IFCHR}; +pub use libc::consts::os::posix88::{S_IFDIR, S_IFIFO, S_IFMT, S_IFREG}; +pub use libc::consts::os::posix88::{S_IREAD, S_IRUSR, S_IRWXU, S_IWUSR}; +pub use libc::consts::os::posix88::{STDERR_FILENO, STDIN_FILENO}; +pub use libc::consts::os::posix88::{STDOUT_FILENO, W_OK, X_OK}; + +pub use libc::funcs::c95::ctype::{isalnum, isalpha, iscntrl, isdigit}; +pub use libc::funcs::c95::ctype::{islower, isprint, ispunct, isspace}; +pub use libc::funcs::c95::ctype::{isupper, isxdigit, tolower, toupper}; + +pub use libc::funcs::c95::stdio::{fclose, feof, ferror, fflush, fgetc}; +pub use libc::funcs::c95::stdio::{fgetpos, fgets, fopen, fputc, fputs}; +pub use libc::funcs::c95::stdio::{fread, freopen, fseek, fsetpos, ftell}; +pub use libc::funcs::c95::stdio::{fwrite, perror, puts, remove, rewind}; +pub use libc::funcs::c95::stdio::{setbuf, setvbuf, tmpfile, ungetc}; + +pub use libc::funcs::c95::stdlib::{abort, abs, atof, atoi, calloc, exit}; +pub use libc::funcs::c95::stdlib::{free, getenv, labs, malloc, rand}; +pub use libc::funcs::c95::stdlib::{realloc, srand, strtod, strtol}; +pub use libc::funcs::c95::stdlib::{strtoul, system}; + +pub use libc::funcs::c95::string::{memchr, memcmp, memcpy, memmove}; +pub use libc::funcs::c95::string::{memset, strcat, strchr, strcmp}; +pub use libc::funcs::c95::string::{strcoll, strcpy, strcspn, strerror}; +pub use libc::funcs::c95::string::{strlen, strncat, strncmp, strncpy}; +pub use libc::funcs::c95::string::{strpbrk, strrchr, strspn, strstr}; +pub use libc::funcs::c95::string::{strtok, strxfrm}; + +pub use libc::funcs::posix88::fcntl::{open, creat}; +pub use libc::funcs::posix88::stat_::{chmod, fstat, mkdir, stat}; +pub use libc::funcs::posix88::stdio::{fdopen, fileno, pclose, popen}; +pub use libc::funcs::posix88::unistd::{access, chdir, close, dup, dup2}; +pub use libc::funcs::posix88::unistd::{execv, execve, execvp, getcwd}; +pub use libc::funcs::posix88::unistd::{getpid, isatty, lseek, pipe, read}; +pub use libc::funcs::posix88::unistd::{rmdir, unlink, write}; + + +pub mod types { + + // Types tend to vary *per architecture* so we pull their definitions out + // into this module. + + // Standard types that are opaque or common, so are not per-target. + pub mod common { + pub mod c95 { + pub enum c_void {} + pub enum FILE {} + pub enum fpos_t {} + } + pub mod c99 { + pub type int8_t = i8; + pub type int16_t = i16; + pub type int32_t = i32; + pub type int64_t = i64; + pub type uint8_t = u8; + pub type uint16_t = u16; + pub type uint32_t = u32; + pub type uint64_t = u64; + } + pub mod posix88 { + pub enum DIR {} + pub enum dirent_t {} + } + pub mod posix01 {} + pub mod posix08 {} + pub mod bsd44 {} + } + + // Standard types that are scalar but vary by OS and arch. + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + pub mod os { + pub mod common { + pub mod posix01 { + use libc::types::common::c95::{c_void}; + use libc::types::os::arch::c95::{c_char, size_t}; + pub struct glob_t { + gl_pathc: size_t, + gl_pathv: **c_char, + gl_offs: size_t, + + __unused1: *c_void, + __unused2: *c_void, + __unused3: *c_void, + __unused4: *c_void, + __unused5: *c_void, + } + } + } + + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + #[cfg(target_arch = "mips")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i32; + pub type c_ulong = u32; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u32; + pub type ptrdiff_t = i32; + pub type clock_t = i32; + pub type time_t = i32; + pub type wchar_t = i32; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i32; + pub type dev_t = u64; + pub type ino_t = u32; + pub type pid_t = i32; + pub type uid_t = u32; + pub type gid_t = u32; + pub type useconds_t = u32; + pub type mode_t = u32; + pub type ssize_t = i32; + } + pub mod posix01 { + use libc::types::os::arch::c95::{c_short, c_long, c_ulong, time_t}; + use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; + use libc::types::os::arch::posix88::{mode_t, off_t}; + use libc::types::os::arch::posix88::{uid_t}; + + pub type nlink_t = u32; + pub type blksize_t = i32; + pub type blkcnt_t = i32; + + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + pub struct stat { + st_dev: dev_t, + __pad1: c_short, + st_ino: ino_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: dev_t, + __pad2: c_short, + st_size: off_t, + st_blksize: blksize_t, + st_blocks: blkcnt_t, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + __unused4: c_long, + __unused5: c_long, + } + + #[cfg(target_arch = "mips")] + pub struct stat { + st_dev: c_ulong, + st_pad1: [c_long, ..3], + st_ino: ino_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: c_ulong, + st_pad2: [c_long, ..2], + st_size: off_t, + st_pad3: c_long, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + st_blksize: blksize_t, + st_blocks: blkcnt_t, + st_pad5: [c_long, ..14], + } + } + pub mod posix08 {} + pub mod bsd44 {} + pub mod extra {} + } + + #[cfg(target_arch = "x86_64")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i64; + pub type c_ulong = u64; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u64; + pub type ptrdiff_t = i64; + pub type clock_t = i64; + pub type time_t = i64; + pub type wchar_t = i32; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i64; + pub type dev_t = u64; + pub type ino_t = u64; + pub type pid_t = i32; + pub type uid_t = u32; + pub type gid_t = u32; + pub type useconds_t = u32; + pub type mode_t = u32; + pub type ssize_t = i64; + } + pub mod posix01 { + use libc::types::os::arch::c95::{c_int, c_long, time_t}; + use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; + use libc::types::os::arch::posix88::{mode_t, off_t}; + use libc::types::os::arch::posix88::{uid_t}; + + pub type nlink_t = u64; + pub type blksize_t = i64; + pub type blkcnt_t = i64; + pub struct stat { + st_dev: dev_t, + st_ino: ino_t, + st_nlink: nlink_t, + st_mode: mode_t, + st_uid: uid_t, + st_gid: gid_t, + __pad0: c_int, + st_rdev: dev_t, + st_size: off_t, + st_blksize: blksize_t, + st_blocks: blkcnt_t, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + __unused: [c_long, ..3], + } + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + } + } + } + + #[cfg(target_os = "freebsd")] + pub mod os { + pub mod common { + pub mod posix01 { + use libc::types::common::c95::{c_void}; + use libc::types::os::arch::c95::{c_char, c_int, size_t}; + pub struct glob_t { + gl_pathc: size_t, + __unused1: size_t, + gl_offs: size_t, + __unused2: c_int, + gl_pathv: **c_char, + + __unused3: *c_void, + + __unused4: *c_void, + __unused5: *c_void, + __unused6: *c_void, + __unused7: *c_void, + __unused8: *c_void, + } + } + } + + #[cfg(target_arch = "x86_64")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i64; + pub type c_ulong = u64; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u64; + pub type ptrdiff_t = i64; + pub type clock_t = i32; + pub type time_t = i64; + pub type wchar_t = i32; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i64; + pub type dev_t = u32; + pub type ino_t = u32; + pub type pid_t = i32; + pub type uid_t = u32; + pub type gid_t = u32; + pub type useconds_t = u32; + pub type mode_t = u16; + pub type ssize_t = i64; + } + pub mod posix01 { + use libc::types::common::c99::{uint8_t, uint32_t, int32_t}; + use libc::types::os::arch::c95::{c_long, time_t}; + use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; + use libc::types::os::arch::posix88::{mode_t, off_t}; + use libc::types::os::arch::posix88::{uid_t}; + + pub type nlink_t = u16; + pub type blksize_t = i64; + pub type blkcnt_t = i64; + pub type fflags_t = u32; + pub struct stat { + st_dev: dev_t, + st_ino: ino_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: dev_t, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + st_size: off_t, + st_blocks: blkcnt_t, + st_blksize: blksize_t, + st_flags: fflags_t, + st_gen: uint32_t, + st_lspare: int32_t, + st_birthtime: time_t, + st_birthtime_nsec: c_long, + __unused: [uint8_t, ..2], + } + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + } + } + } + + #[cfg(target_os = "win32")] + pub mod os { + pub mod common { + pub mod posix01 { + use libc::types::os::arch::c95::{c_int, c_short}; + use libc::types::os::arch::extra::{int64, time64_t}; + use libc::types::os::arch::posix88::{dev_t, ino_t}; + use libc::types::os::arch::posix88::mode_t; + + // Note: this is the struct called stat64 in win32. Not stat, + // nor stati64. + pub struct stat { + st_dev: dev_t, + st_ino: ino_t, + st_mode: mode_t, + st_nlink: c_short, + st_uid: c_short, + st_gid: c_short, + st_rdev: dev_t, + st_size: int64, + st_atime: time64_t, + st_mtime: time64_t, + st_ctime: time64_t, + } + } + } + + #[cfg(target_arch = "x86")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i32; + pub type c_ulong = u32; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u32; + pub type ptrdiff_t = i32; + pub type clock_t = i32; + pub type time_t = i32; + pub type wchar_t = u16; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i32; + pub type dev_t = u32; + pub type ino_t = i16; + pub type pid_t = i32; + pub type useconds_t = u32; + pub type mode_t = u16; + pub type ssize_t = i32; + } + pub mod posix01 { + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + use libc::types::common::c95::c_void; + use libc::types::os::arch::c95::{c_char, c_int, c_uint}; + use libc::types::os::arch::c95::{c_long, c_ulong}; + use libc::types::os::arch::c95::{wchar_t}; + use libc::types::os::arch::c99::{c_ulonglong}; + + pub type BOOL = c_int; + pub type BYTE = u8; + pub type CCHAR = c_char; + pub type CHAR = c_char; + + pub type DWORD = c_ulong; + pub type DWORDLONG = c_ulonglong; + + pub type HANDLE = LPVOID; + pub type HMODULE = c_uint; + + pub type LONG_PTR = c_long; + + pub type LPCWSTR = *WCHAR; + pub type LPCSTR = *CHAR; + pub type LPCTSTR = *CHAR; + pub type LPTCH = *CHAR; + + pub type LPWSTR = *mut WCHAR; + pub type LPSTR = *mut CHAR; + pub type LPTSTR = *mut CHAR; + + // Not really, but opaque to us. + pub type LPSECURITY_ATTRIBUTES = LPVOID; + + pub type LPVOID = *mut c_void; + pub type LPBYTE = *mut BYTE; + pub type LPWORD = *mut WORD; + pub type LPDWORD = *mut DWORD; + pub type LPHANDLE = *mut HANDLE; + + pub type LRESULT = LONG_PTR; + pub type PBOOL = *mut BOOL; + pub type WCHAR = wchar_t; + pub type WORD = u16; + + pub type time64_t = i64; + pub type int64 = i64; + + pub struct STARTUPINFO { + cb: DWORD, + lpReserved: LPTSTR, + lpDesktop: LPTSTR, + lpTitle: LPTSTR, + dwX: DWORD, + dwY: DWORD, + dwXSize: DWORD, + dwYSize: DWORD, + dwXCountChars: DWORD, + dwYCountCharts: DWORD, + dwFillAttribute: DWORD, + dwFlags: DWORD, + wShowWindow: WORD, + cbReserved2: WORD, + lpReserved2: LPBYTE, + hStdInput: HANDLE, + hStdOutput: HANDLE, + hStdError: HANDLE + } + pub type LPSTARTUPINFO = *mut STARTUPINFO; + + pub struct PROCESS_INFORMATION { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD + } + pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; + } + } + } + + #[cfg(target_os = "macos")] + pub mod os { + pub mod common { + pub mod posix01 { + use libc::types::common::c95::{c_void}; + use libc::types::os::arch::c95::{c_char, c_int, size_t}; + pub struct glob_t { + gl_pathc: size_t, + __unused1: c_int, + gl_offs: size_t, + __unused2: c_int, + gl_pathv: **c_char, + + __unused3: *c_void, + + __unused4: *c_void, + __unused5: *c_void, + __unused6: *c_void, + __unused7: *c_void, + __unused8: *c_void, + } + } + } + + #[cfg(target_arch = "x86")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i32; + pub type c_ulong = u32; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u32; + pub type ptrdiff_t = i32; + pub type clock_t = u32; + pub type time_t = i32; + pub type wchar_t = i32; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i64; + pub type dev_t = i32; + pub type ino_t = u64; + pub type pid_t = i32; + pub type uid_t = u32; + pub type gid_t = u32; + pub type useconds_t = u32; + pub type mode_t = u16; + pub type ssize_t = i32; + } + pub mod posix01 { + use libc::types::common::c99::{int32_t, int64_t, uint32_t}; + use libc::types::os::arch::c95::{c_long, time_t}; + use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t, + mode_t, off_t, uid_t}; + + pub type nlink_t = u16; + pub type blksize_t = i64; + pub type blkcnt_t = i32; + + pub struct stat { + st_dev: dev_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_ino: ino_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: dev_t, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + st_birthtime: time_t, + st_birthtime_nsec: c_long, + st_size: off_t, + st_blocks: blkcnt_t, + st_blksize: blksize_t, + st_flags: uint32_t, + st_gen: uint32_t, + st_lspare: int32_t, + st_qspare: [int64_t, ..2], + } + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + } + } + + #[cfg(target_arch = "x86_64")] + pub mod arch { + pub mod c95 { + pub type c_char = i8; + pub type c_schar = i8; + pub type c_uchar = u8; + pub type c_short = i16; + pub type c_ushort = u16; + pub type c_int = i32; + pub type c_uint = u32; + pub type c_long = i64; + pub type c_ulong = u64; + pub type c_float = f32; + pub type c_double = f64; + pub type size_t = u64; + pub type ptrdiff_t = i64; + pub type clock_t = u64; + pub type time_t = i64; + pub type wchar_t = i32; + } + pub mod c99 { + pub type c_longlong = i64; + pub type c_ulonglong = u64; + pub type intptr_t = int; + pub type uintptr_t = uint; + } + pub mod posix88 { + pub type off_t = i64; + pub type dev_t = i32; + pub type ino_t = u64; + pub type pid_t = i32; + pub type uid_t = u32; + pub type gid_t = u32; + pub type useconds_t = u32; + pub type mode_t = u16; + pub type ssize_t = i64; + } + pub mod posix01 { + use libc::types::common::c99::{int32_t, int64_t}; + use libc::types::common::c99::{uint32_t}; + use libc::types::os::arch::c95::{c_long, time_t}; + use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; + use libc::types::os::arch::posix88::{mode_t, off_t, uid_t}; + + pub type nlink_t = u16; + pub type blksize_t = i64; + pub type blkcnt_t = i32; + + pub struct stat { + st_dev: dev_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_ino: ino_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: dev_t, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + st_birthtime: time_t, + st_birthtime_nsec: c_long, + st_size: off_t, + st_blocks: blkcnt_t, + st_blksize: blksize_t, + st_flags: uint32_t, + st_gen: uint32_t, + st_lspare: int32_t, + st_qspare: [int64_t, ..2], + } + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + } + } + } +} + +pub mod consts { + // Consts tend to vary per OS so we pull their definitions out + // into this module. + + #[cfg(target_os = "win32")] + pub mod os { + pub mod c95 { + pub static EXIT_FAILURE : int = 1; + pub static EXIT_SUCCESS : int = 0; + pub static RAND_MAX : int = 32767; + pub static EOF : int = -1; + pub static SEEK_SET : int = 0; + pub static SEEK_CUR : int = 1; + pub static SEEK_END : int = 2; + pub static _IOFBF : int = 0; + pub static _IONBF : int = 4; + pub static _IOLBF : int = 64; + pub static BUFSIZ : uint = 512_u; + pub static FOPEN_MAX : uint = 20_u; + pub static FILENAME_MAX : uint = 260_u; + pub static L_tmpnam : uint = 16_u; + pub static TMP_MAX : uint = 32767_u; + } + pub mod c99 { + } + pub mod posix88 { + pub static O_RDONLY : int = 0; + pub static O_WRONLY : int = 1; + pub static O_RDWR : int = 2; + pub static O_APPEND : int = 8; + pub static O_CREAT : int = 256; + pub static O_EXCL : int = 1024; + pub static O_TRUNC : int = 512; + pub static S_IFIFO : int = 4096; + pub static S_IFCHR : int = 8192; + pub static S_IFBLK : int = 12288; + pub static S_IFDIR : int = 16384; + pub static S_IFREG : int = 32768; + pub static S_IFMT : int = 61440; + pub static S_IEXEC : int = 64; + pub static S_IWRITE : int = 128; + pub static S_IREAD : int = 256; + pub static S_IRWXU : int = 448; + pub static S_IXUSR : int = 64; + pub static S_IWUSR : int = 128; + pub static S_IRUSR : int = 256; + pub static F_OK : int = 0; + pub static R_OK : int = 4; + pub static W_OK : int = 2; + pub static X_OK : int = 1; + pub static STDIN_FILENO : int = 0; + pub static STDOUT_FILENO : int = 1; + pub static STDERR_FILENO : int = 2; + } + pub mod posix01 { + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + use libc::types::os::arch::extra::{DWORD, BOOL}; + + pub static TRUE : BOOL = 1; + pub static FALSE : BOOL = 0; + + pub static O_TEXT : int = 16384; + pub static O_BINARY : int = 32768; + pub static O_NOINHERIT: int = 128; + + pub static ERROR_SUCCESS : int = 0; + pub static ERROR_INSUFFICIENT_BUFFER : int = 122; + pub static INVALID_HANDLE_VALUE: int = -1; + + pub static DELETE : DWORD = 0x00010000; + pub static READ_CONTROL : DWORD = 0x00020000; + pub static SYNCHRONIZE : DWORD = 0x00100000; + pub static WRITE_DAC : DWORD = 0x00040000; + pub static WRITE_OWNER : DWORD = 0x00080000; + + pub static PROCESS_CREATE_PROCESS : DWORD = 0x0080; + pub static PROCESS_CREATE_THREAD : DWORD = 0x0002; + pub static PROCESS_DUP_HANDLE : DWORD = 0x0040; + pub static PROCESS_QUERY_INFORMATION : DWORD = 0x0400; + pub static PROCESS_QUERY_LIMITED_INFORMATION : DWORD = 0x1000; + pub static PROCESS_SET_INFORMATION : DWORD = 0x0200; + pub static PROCESS_SET_QUOTA : DWORD = 0x0100; + pub static PROCESS_SUSPEND_RESUME : DWORD = 0x0800; + pub static PROCESS_TERMINATE : DWORD = 0x0001; + pub static PROCESS_VM_OPERATION : DWORD = 0x0008; + pub static PROCESS_VM_READ : DWORD = 0x0010; + pub static PROCESS_VM_WRITE : DWORD = 0x0020; + + pub static STARTF_FORCEONFEEDBACK : DWORD = 0x00000040; + pub static STARTF_FORCEOFFFEEDBACK : DWORD = 0x00000080; + pub static STARTF_PREVENTPINNING : DWORD = 0x00002000; + pub static STARTF_RUNFULLSCREEN : DWORD = 0x00000020; + pub static STARTF_TITLEISAPPID : DWORD = 0x00001000; + pub static STARTF_TITLEISLINKNAME : DWORD = 0x00000800; + pub static STARTF_USECOUNTCHARS : DWORD = 0x00000008; + pub static STARTF_USEFILLATTRIBUTE : DWORD = 0x00000010; + pub static STARTF_USEHOTKEY : DWORD = 0x00000200; + pub static STARTF_USEPOSITION : DWORD = 0x00000004; + pub static STARTF_USESHOWWINDOW : DWORD = 0x00000001; + pub static STARTF_USESIZE : DWORD = 0x00000002; + pub static STARTF_USESTDHANDLES : DWORD = 0x00000100; + + pub static WAIT_ABANDONED : DWORD = 0x00000080; + pub static WAIT_OBJECT_0 : DWORD = 0x00000000; + pub static WAIT_TIMEOUT : DWORD = 0x00000102; + pub static WAIT_FAILED : DWORD = -1; + + pub static DUPLICATE_CLOSE_SOURCE : DWORD = 0x00000001; + pub static DUPLICATE_SAME_ACCESS : DWORD = 0x00000002; + + pub static INFINITE : DWORD = -1; + pub static STILL_ACTIVE : DWORD = 259; + } + } + + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + pub mod os { + pub mod c95 { + pub static EXIT_FAILURE : int = 1; + pub static EXIT_SUCCESS : int = 0; + pub static RAND_MAX : int = 2147483647; + pub static EOF : int = -1; + pub static SEEK_SET : int = 0; + pub static SEEK_CUR : int = 1; + pub static SEEK_END : int = 2; + pub static _IOFBF : int = 0; + pub static _IONBF : int = 2; + pub static _IOLBF : int = 1; + pub static BUFSIZ : uint = 8192_u; + pub static FOPEN_MAX : uint = 16_u; + pub static FILENAME_MAX : uint = 4096_u; + pub static L_tmpnam : uint = 20_u; + pub static TMP_MAX : uint = 238328_u; + } + pub mod c99 { + } + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "x86_64")] + #[cfg(target_arch = "arm")] + pub mod posix88 { + pub static O_RDONLY : int = 0; + pub static O_WRONLY : int = 1; + pub static O_RDWR : int = 2; + pub static O_APPEND : int = 1024; + pub static O_CREAT : int = 64; + pub static O_EXCL : int = 128; + pub static O_TRUNC : int = 512; + pub static S_IFIFO : int = 4096; + pub static S_IFCHR : int = 8192; + pub static S_IFBLK : int = 24576; + pub static S_IFDIR : int = 16384; + pub static S_IFREG : int = 32768; + pub static S_IFMT : int = 61440; + pub static S_IEXEC : int = 64; + pub static S_IWRITE : int = 128; + pub static S_IREAD : int = 256; + pub static S_IRWXU : int = 448; + pub static S_IXUSR : int = 64; + pub static S_IWUSR : int = 128; + pub static S_IRUSR : int = 256; + pub static F_OK : int = 0; + pub static R_OK : int = 4; + pub static W_OK : int = 2; + pub static X_OK : int = 1; + pub static STDIN_FILENO : int = 0; + pub static STDOUT_FILENO : int = 1; + pub static STDERR_FILENO : int = 2; + pub static F_LOCK : int = 1; + pub static F_TEST : int = 3; + pub static F_TLOCK : int = 2; + pub static F_ULOCK : int = 0; + pub static SIGHUP : int = 1; + pub static SIGINT : int = 2; + pub static SIGQUIT : int = 3; + pub static SIGILL : int = 4; + pub static SIGABRT : int = 6; + pub static SIGFPE : int = 8; + pub static SIGKILL : int = 9; + pub static SIGSEGV : int = 11; + pub static SIGPIPE : int = 13; + pub static SIGALRM : int = 14; + pub static SIGTERM : int = 15; + } + #[cfg(target_arch = "mips")] + pub mod posix88 { + pub static O_RDONLY : int = 0; + pub static O_WRONLY : int = 1; + pub static O_RDWR : int = 2; + pub static O_APPEND : int = 8; + pub static O_CREAT : int = 256; + pub static O_EXCL : int = 1024; + pub static O_TRUNC : int = 512; + pub static S_IFIFO : int = 4096; + pub static S_IFCHR : int = 8192; + pub static S_IFBLK : int = 24576; + pub static S_IFDIR : int = 16384; + pub static S_IFREG : int = 32768; + pub static S_IFMT : int = 61440; + pub static S_IEXEC : int = 64; + pub static S_IWRITE : int = 128; + pub static S_IREAD : int = 256; + pub static S_IRWXU : int = 448; + pub static S_IXUSR : int = 64; + pub static S_IWUSR : int = 128; + pub static S_IRUSR : int = 256; + pub static F_OK : int = 0; + pub static R_OK : int = 4; + pub static W_OK : int = 2; + pub static X_OK : int = 1; + pub static STDIN_FILENO : int = 0; + pub static STDOUT_FILENO : int = 1; + pub static STDERR_FILENO : int = 2; + pub static F_LOCK : int = 1; + pub static F_TEST : int = 3; + pub static F_TLOCK : int = 2; + pub static F_ULOCK : int = 0; + pub static SIGHUP : int = 1; + pub static SIGINT : int = 2; + pub static SIGQUIT : int = 3; + pub static SIGILL : int = 4; + pub static SIGABRT : int = 6; + pub static SIGFPE : int = 8; + pub static SIGKILL : int = 9; + pub static SIGSEGV : int = 11; + pub static SIGPIPE : int = 13; + pub static SIGALRM : int = 14; + pub static SIGTERM : int = 15; + } + pub mod posix01 { + pub static SIGTRAP : int = 5; + + pub static GLOB_ERR : int = 1 << 0; + pub static GLOB_MARK : int = 1 << 1; + pub static GLOB_NOSORT : int = 1 << 2; + pub static GLOB_DOOFFS : int = 1 << 3; + pub static GLOB_NOCHECK : int = 1 << 4; + pub static GLOB_APPEND : int = 1 << 5; + pub static GLOB_NOESCAPE : int = 1 << 6; + + pub static GLOB_NOSPACE : int = 1; + pub static GLOB_ABORTED : int = 2; + pub static GLOB_NOMATCH : int = 3; + } + pub mod posix08 { + } + pub mod bsd44 { + } + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "x86_64")] + #[cfg(target_arch = "arm")] + pub mod extra { + pub static O_RSYNC : int = 1052672; + pub static O_DSYNC : int = 4096; + pub static O_SYNC : int = 1052672; + } + #[cfg(target_arch = "mips")] + pub mod extra { + pub static O_RSYNC : int = 16400; + pub static O_DSYNC : int = 16; + pub static O_SYNC : int = 16400; + } + } + + #[cfg(target_os = "freebsd")] + pub mod os { + pub mod c95 { + pub static EXIT_FAILURE : int = 1; + pub static EXIT_SUCCESS : int = 0; + pub static RAND_MAX : int = 2147483647; + pub static EOF : int = -1; + pub static SEEK_SET : int = 0; + pub static SEEK_CUR : int = 1; + pub static SEEK_END : int = 2; + pub static _IOFBF : int = 0; + pub static _IONBF : int = 2; + pub static _IOLBF : int = 1; + pub static BUFSIZ : uint = 1024_u; + pub static FOPEN_MAX : uint = 20_u; + pub static FILENAME_MAX : uint = 1024_u; + pub static L_tmpnam : uint = 1024_u; + pub static TMP_MAX : uint = 308915776_u; + } + pub mod c99 { + } + pub mod posix88 { + pub static O_RDONLY : int = 0; + pub static O_WRONLY : int = 1; + pub static O_RDWR : int = 2; + pub static O_APPEND : int = 8; + pub static O_CREAT : int = 512; + pub static O_EXCL : int = 2048; + pub static O_TRUNC : int = 1024; + pub static S_IFIFO : int = 4096; + pub static S_IFCHR : int = 8192; + pub static S_IFBLK : int = 24576; + pub static S_IFDIR : int = 16384; + pub static S_IFREG : int = 32768; + pub static S_IFMT : int = 61440; + pub static S_IEXEC : int = 64; + pub static S_IWRITE : int = 128; + pub static S_IREAD : int = 256; + pub static S_IRWXU : int = 448; + pub static S_IXUSR : int = 64; + pub static S_IWUSR : int = 128; + pub static S_IRUSR : int = 256; + pub static F_OK : int = 0; + pub static R_OK : int = 4; + pub static W_OK : int = 2; + pub static X_OK : int = 1; + pub static STDIN_FILENO : int = 0; + pub static STDOUT_FILENO : int = 1; + pub static STDERR_FILENO : int = 2; + pub static F_LOCK : int = 1; + pub static F_TEST : int = 3; + pub static F_TLOCK : int = 2; + pub static F_ULOCK : int = 0; + pub static SIGHUP : int = 1; + pub static SIGINT : int = 2; + pub static SIGQUIT : int = 3; + pub static SIGILL : int = 4; + pub static SIGABRT : int = 6; + pub static SIGFPE : int = 8; + pub static SIGKILL : int = 9; + pub static SIGSEGV : int = 11; + pub static SIGPIPE : int = 13; + pub static SIGALRM : int = 14; + pub static SIGTERM : int = 15; + } + pub mod posix01 { + pub static SIGTRAP : int = 5; + + pub static GLOB_APPEND : int = 0x0001; + pub static GLOB_DOOFFS : int = 0x0002; + pub static GLOB_ERR : int = 0x0004; + pub static GLOB_MARK : int = 0x0008; + pub static GLOB_NOCHECK : int = 0x0010; + pub static GLOB_NOSORT : int = 0x0020; + pub static GLOB_NOESCAPE : int = 0x2000; + + pub static GLOB_NOSPACE : int = -1; + pub static GLOB_ABORTED : int = -2; + pub static GLOB_NOMATCH : int = -3; + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + pub static O_SYNC : int = 128; + pub static CTL_KERN: int = 1; + pub static KERN_PROC: int = 14; + pub static KERN_PROC_PATHNAME: int = 12; + } + } + + #[cfg(target_os = "macos")] + pub mod os { + pub mod c95 { + pub static EXIT_FAILURE : int = 1; + pub static EXIT_SUCCESS : int = 0; + pub static RAND_MAX : int = 2147483647; + pub static EOF : int = -1; + pub static SEEK_SET : int = 0; + pub static SEEK_CUR : int = 1; + pub static SEEK_END : int = 2; + pub static _IOFBF : int = 0; + pub static _IONBF : int = 2; + pub static _IOLBF : int = 1; + pub static BUFSIZ : uint = 1024_u; + pub static FOPEN_MAX : uint = 20_u; + pub static FILENAME_MAX : uint = 1024_u; + pub static L_tmpnam : uint = 1024_u; + pub static TMP_MAX : uint = 308915776_u; + } + pub mod c99 { + } + pub mod posix88 { + pub static O_RDONLY : int = 0; + pub static O_WRONLY : int = 1; + pub static O_RDWR : int = 2; + pub static O_APPEND : int = 8; + pub static O_CREAT : int = 512; + pub static O_EXCL : int = 2048; + pub static O_TRUNC : int = 1024; + pub static S_IFIFO : int = 4096; + pub static S_IFCHR : int = 8192; + pub static S_IFBLK : int = 24576; + pub static S_IFDIR : int = 16384; + pub static S_IFREG : int = 32768; + pub static S_IFMT : int = 61440; + pub static S_IEXEC : int = 64; + pub static S_IWRITE : int = 128; + pub static S_IREAD : int = 256; + pub static S_IRWXU : int = 448; + pub static S_IXUSR : int = 64; + pub static S_IWUSR : int = 128; + pub static S_IRUSR : int = 256; + pub static F_OK : int = 0; + pub static R_OK : int = 4; + pub static W_OK : int = 2; + pub static X_OK : int = 1; + pub static STDIN_FILENO : int = 0; + pub static STDOUT_FILENO : int = 1; + pub static STDERR_FILENO : int = 2; + pub static F_LOCK : int = 1; + pub static F_TEST : int = 3; + pub static F_TLOCK : int = 2; + pub static F_ULOCK : int = 0; + pub static SIGHUP : int = 1; + pub static SIGINT : int = 2; + pub static SIGQUIT : int = 3; + pub static SIGILL : int = 4; + pub static SIGABRT : int = 6; + pub static SIGFPE : int = 8; + pub static SIGKILL : int = 9; + pub static SIGSEGV : int = 11; + pub static SIGPIPE : int = 13; + pub static SIGALRM : int = 14; + pub static SIGTERM : int = 15; + } + pub mod posix01 { + pub static SIGTRAP : int = 5; + + pub static GLOB_APPEND : int = 0x0001; + pub static GLOB_DOOFFS : int = 0x0002; + pub static GLOB_ERR : int = 0x0004; + pub static GLOB_MARK : int = 0x0008; + pub static GLOB_NOCHECK : int = 0x0010; + pub static GLOB_NOSORT : int = 0x0020; + pub static GLOB_NOESCAPE : int = 0x2000; + + pub static GLOB_NOSPACE : int = -1; + pub static GLOB_ABORTED : int = -2; + pub static GLOB_NOMATCH : int = -3; + } + pub mod posix08 { + } + pub mod bsd44 { + } + pub mod extra { + pub static O_DSYNC : int = 4194304; + pub static O_SYNC : int = 128; + pub static F_FULLFSYNC : int = 51; + } + } +} + + +pub mod funcs { + // Thankfull most of c95 is universally available and does not vary by OS + // or anything. The same is not true of POSIX. + + pub mod c95 { + #[nolink] + #[abi = "cdecl"] + pub mod ctype { + use libc::types::os::arch::c95::{c_char, c_int}; + + pub extern { + unsafe fn isalnum(c: c_int) -> c_int; + unsafe fn isalpha(c: c_int) -> c_int; + unsafe fn iscntrl(c: c_int) -> c_int; + unsafe fn isdigit(c: c_int) -> c_int; + unsafe fn isgraph(c: c_int) -> c_int; + unsafe fn islower(c: c_int) -> c_int; + unsafe fn isprint(c: c_int) -> c_int; + unsafe fn ispunct(c: c_int) -> c_int; + unsafe fn isspace(c: c_int) -> c_int; + unsafe fn isupper(c: c_int) -> c_int; + unsafe fn isxdigit(c: c_int) -> c_int; + unsafe fn tolower(c: c_char) -> c_char; + unsafe fn toupper(c: c_char) -> c_char; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod stdio { + use libc::types::common::c95::{FILE, c_void, fpos_t}; + use libc::types::os::arch::c95::{c_char, c_int, c_long, size_t}; + + pub extern { + unsafe fn fopen(filename: *c_char, mode: *c_char) -> *FILE; + unsafe fn freopen(filename: *c_char, mode: *c_char, + file: *FILE) -> *FILE; + unsafe fn fflush(file: *FILE) -> c_int; + unsafe fn fclose(file: *FILE) -> c_int; + unsafe fn remove(filename: *c_char) -> c_int; + unsafe fn rename(oldname: *c_char, newname: *c_char) -> c_int; + unsafe fn tmpfile() -> *FILE; + unsafe fn setvbuf(stream: *FILE, buffer: *c_char, + mode: c_int, size: size_t) -> c_int; + unsafe fn setbuf(stream: *FILE, buf: *c_char); + // Omitted: printf and scanf variants. + unsafe fn fgetc(stream: *FILE) -> c_int; + #[fast_ffi] + unsafe fn fgets(buf: *mut c_char, n: c_int, + stream: *FILE) -> *c_char; + #[fast_ffi] + unsafe fn fputc(c: c_int, stream: *FILE) -> c_int; + #[fast_ffi] + unsafe fn fputs(s: *c_char, stream: *FILE) -> *c_char; + // Omitted: getc, getchar (might be macros). + + // Omitted: gets, so ridiculously unsafe that it should not + // survive. + + // Omitted: putc, putchar (might be macros). + unsafe fn puts(s: *c_char) -> c_int; + unsafe fn ungetc(c: c_int, stream: *FILE) -> c_int; + #[fast_ffi] + unsafe fn fread(ptr: *mut c_void, size: size_t, + nobj: size_t, stream: *FILE) -> size_t; + #[fast_ffi] + unsafe fn fwrite(ptr: *c_void, size: size_t, + nobj: size_t, stream: *FILE) -> size_t; + unsafe fn fseek(stream: *FILE, offset: c_long, + whence: c_int) -> c_int; + unsafe fn ftell(stream: *FILE) -> c_long; + unsafe fn rewind(stream: *FILE); + unsafe fn fgetpos(stream: *FILE, ptr: *fpos_t) -> c_int; + unsafe fn fsetpos(stream: *FILE, ptr: *fpos_t) -> c_int; + unsafe fn feof(stream: *FILE) -> c_int; + unsafe fn ferror(stream: *FILE) -> c_int; + unsafe fn perror(s: *c_char); + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod stdlib { + use libc::types::common::c95::c_void; + use libc::types::os::arch::c95::{c_char, c_double, c_int}; + use libc::types::os::arch::c95::{c_long, c_uint, c_ulong}; + use libc::types::os::arch::c95::{size_t}; + + pub extern { + unsafe fn abs(i: c_int) -> c_int; + unsafe fn labs(i: c_long) -> c_long; + // Omitted: div, ldiv (return pub type incomplete). + unsafe fn atof(s: *c_char) -> c_double; + unsafe fn atoi(s: *c_char) -> c_int; + unsafe fn strtod(s: *c_char, endp: **c_char) -> c_double; + unsafe fn strtol(s: *c_char, endp: **c_char, base: c_int) + -> c_long; + unsafe fn strtoul(s: *c_char, endp: **c_char, base: c_int) + -> c_ulong; + #[fast_ffi] + unsafe fn calloc(nobj: size_t, size: size_t) -> *c_void; + #[fast_ffi] + unsafe fn malloc(size: size_t) -> *c_void; + #[fast_ffi] + unsafe fn realloc(p: *c_void, size: size_t) -> *c_void; + #[fast_ffi] + unsafe fn free(p: *c_void); + unsafe fn abort() -> !; + unsafe fn exit(status: c_int) -> !; + // Omitted: atexit. + unsafe fn system(s: *c_char) -> c_int; + unsafe fn getenv(s: *c_char) -> *c_char; + // Omitted: bsearch, qsort + unsafe fn rand() -> c_int; + unsafe fn srand(seed: c_uint); + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod string { + use libc::types::common::c95::c_void; + use libc::types::os::arch::c95::{c_char, c_int, size_t}; + use libc::types::os::arch::c95::{wchar_t}; + + pub extern { + unsafe fn strcpy(dst: *c_char, src: *c_char) -> *c_char; + unsafe fn strncpy(dst: *c_char, src: *c_char, n: size_t) + -> *c_char; + unsafe fn strcat(s: *c_char, ct: *c_char) -> *c_char; + unsafe fn strncat(s: *c_char, ct: *c_char, n: size_t) + -> *c_char; + unsafe fn strcmp(cs: *c_char, ct: *c_char) -> c_int; + unsafe fn strncmp(cs: *c_char, ct: *c_char, n: size_t) + -> c_int; + unsafe fn strcoll(cs: *c_char, ct: *c_char) -> c_int; + unsafe fn strchr(cs: *c_char, c: c_int) -> *c_char; + unsafe fn strrchr(cs: *c_char, c: c_int) -> *c_char; + unsafe fn strspn(cs: *c_char, ct: *c_char) -> size_t; + unsafe fn strcspn(cs: *c_char, ct: *c_char) -> size_t; + unsafe fn strpbrk(cs: *c_char, ct: *c_char) -> *c_char; + unsafe fn strstr(cs: *c_char, ct: *c_char) -> *c_char; + unsafe fn strlen(cs: *c_char) -> size_t; + unsafe fn strerror(n: c_int) -> *c_char; + unsafe fn strtok(s: *c_char, t: *c_char) -> *c_char; + unsafe fn strxfrm(s: *c_char, ct: *c_char, n: size_t) + -> size_t; + unsafe fn wcslen(buf: *wchar_t) -> size_t; + + // These are fine to execute on the Rust stack. They must be, + // in fact, because LLVM generates calls to them! + #[rust_stack] + #[inline(always)] + unsafe fn memcpy(s: *c_void, ct: *c_void, n: size_t) + -> *c_void; + #[rust_stack] + #[inline(always)] + unsafe fn memmove(s: *c_void, ct: *c_void, n: size_t) + -> *c_void; + #[rust_stack] + #[inline(always)] + unsafe fn memcmp(cx: *c_void, ct: *c_void, n: size_t) + -> c_int; + #[rust_stack] + #[inline(always)] + unsafe fn memchr(cx: *c_void, c: c_int, n: size_t) -> *c_void; + #[rust_stack] + #[inline(always)] + unsafe fn memset(s: *c_void, c: c_int, n: size_t) -> *c_void; + } + } + } + + // Microsoft helpfully underscore-qualifies all of its POSIX-like symbols + // to make sure you don't use them accidentally. It also randomly deviates + // from the exact signatures you might otherwise expect, and omits much, + // so be careful when trying to write portable code; it won't always work + // with the same POSIX functions and types as other platforms. + + #[cfg(target_os = "win32")] + pub mod posix88 { + #[nolink] + #[abi = "cdecl"] + pub mod stat_ { + use libc::types::os::common::posix01::stat; + use libc::types::os::arch::c95::{c_int, c_char}; + + pub extern { + #[link_name = "_chmod"] + unsafe fn chmod(path: *c_char, mode: c_int) -> c_int; + + #[link_name = "_mkdir"] + unsafe fn mkdir(path: *c_char) -> c_int; + + #[link_name = "_fstat64"] + unsafe fn fstat(fildes: c_int, buf: *mut stat) -> c_int; + + #[link_name = "_stat64"] + unsafe fn stat(path: *c_char, buf: *mut stat) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod stdio { + use libc::types::common::c95::FILE; + use libc::types::os::arch::c95::{c_int, c_char}; + + pub extern { + #[link_name = "_popen"] + unsafe fn popen(command: *c_char, mode: *c_char) -> *FILE; + + #[link_name = "_pclose"] + unsafe fn pclose(stream: *FILE) -> c_int; + + #[link_name = "_fdopen"] + #[fast_ffi] + unsafe fn fdopen(fd: c_int, mode: *c_char) -> *FILE; + + #[link_name = "_fileno"] + unsafe fn fileno(stream: *FILE) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod fcntl { + use libc::types::os::arch::c95::{c_int, c_char}; + pub extern { + #[link_name = "_open"] + unsafe fn open(path: *c_char, oflag: c_int, mode: c_int) + -> c_int; + + #[link_name = "_creat"] + unsafe fn creat(path: *c_char, mode: c_int) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod dirent { + // Not supplied at all. + } + + #[nolink] + #[abi = "cdecl"] + pub mod unistd { + use libc::types::common::c95::c_void; + use libc::types::os::arch::c95::{c_int, c_uint, c_char, + c_long, size_t}; + use libc::types::os::arch::c99::intptr_t; + + pub extern { + #[link_name = "_access"] + unsafe fn access(path: *c_char, amode: c_int) -> c_int; + + #[link_name = "_chdir"] + unsafe fn chdir(dir: *c_char) -> c_int; + + #[link_name = "_close"] + unsafe fn close(fd: c_int) -> c_int; + + #[link_name = "_dup"] + unsafe fn dup(fd: c_int) -> c_int; + + #[link_name = "_dup2"] + unsafe fn dup2(src: c_int, dst: c_int) -> c_int; + + #[link_name = "_execv"] + unsafe fn execv(prog: *c_char, argv: **c_char) -> intptr_t; + + #[link_name = "_execve"] + unsafe fn execve(prog: *c_char, argv: **c_char, + envp: **c_char) -> c_int; + + #[link_name = "_execvp"] + unsafe fn execvp(c: *c_char, argv: **c_char) -> c_int; + + #[link_name = "_execvpe"] + unsafe fn execvpe(c: *c_char, argv: **c_char, + envp: **c_char) -> c_int; + + #[link_name = "_getcwd"] + unsafe fn getcwd(buf: *c_char, size: size_t) -> *c_char; + + #[link_name = "_getpid"] + unsafe fn getpid() -> c_int; + + #[link_name = "_isatty"] + unsafe fn isatty(fd: c_int) -> c_int; + + #[link_name = "_lseek"] + unsafe fn lseek(fd: c_int, offset: c_long, origin: c_int) + -> c_long; + + #[link_name = "_pipe"] + unsafe fn pipe(fds: *mut c_int, psize: c_uint, + textmode: c_int) -> c_int; + + #[link_name = "_read"] + #[fast_ffi] + unsafe fn read(fd: c_int, buf: *mut c_void, count: c_uint) + -> c_int; + + #[link_name = "_rmdir"] + unsafe fn rmdir(path: *c_char) -> c_int; + + #[link_name = "_unlink"] + unsafe fn unlink(c: *c_char) -> c_int; + + #[link_name = "_write"] + #[fast_ffi] + unsafe fn write(fd: c_int, buf: *c_void, count: c_uint) + -> c_int; + } + } + } + + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + pub mod posix88 { + pub mod stat_ { + use libc::types::os::arch::c95::{c_char, c_int}; + use libc::types::os::arch::posix01::stat; + use libc::types::os::arch::posix88::mode_t; + + #[nolink] + #[abi = "cdecl"] + pub extern { + unsafe fn chmod(path: *c_char, mode: mode_t) -> c_int; + unsafe fn fchmod(fd: c_int, mode: mode_t) -> c_int; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "freebsd")] + #[cfg(target_os = "android")] + unsafe fn fstat(fildes: c_int, buf: *mut stat) -> c_int; + + #[cfg(target_os = "macos")] + #[link_name = "fstat64"] + unsafe fn fstat(fildes: c_int, buf: *mut stat) -> c_int; + + unsafe fn mkdir(path: *c_char, mode: mode_t) -> c_int; + unsafe fn mkfifo(path: *c_char, mode: mode_t) -> c_int; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "freebsd")] + #[cfg(target_os = "android")] + unsafe fn stat(path: *c_char, buf: *mut stat) -> c_int; + + #[cfg(target_os = "macos")] + #[link_name = "stat64"] + unsafe fn stat(path: *c_char, buf: *mut stat) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod stdio { + use libc::types::common::c95::FILE; + use libc::types::os::arch::c95::{c_char, c_int}; + + pub extern { + unsafe fn popen(command: *c_char, mode: *c_char) -> *FILE; + unsafe fn pclose(stream: *FILE) -> c_int; + unsafe fn fdopen(fd: c_int, mode: *c_char) -> *FILE; + unsafe fn fileno(stream: *FILE) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod fcntl { + use libc::types::os::arch::c95::{c_char, c_int}; + use libc::types::os::arch::posix88::mode_t; + + pub extern { + unsafe fn open(path: *c_char, oflag: c_int, mode: c_int) + -> c_int; + unsafe fn creat(path: *c_char, mode: mode_t) -> c_int; + unsafe fn fcntl(fd: c_int, cmd: c_int) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod dirent { + use libc::types::common::posix88::{DIR, dirent_t}; + use libc::types::os::arch::c95::{c_char, c_int, c_long}; + + // NB: On OS X opendir and readdir have two versions, + // one for 32-bit kernelspace and one for 64. + // We should be linking to the 64-bit ones, called + // opendir$INODE64, etc. but for some reason rustc + // doesn't link it correctly on i686, so we're going + // through a C function that mysteriously does work. + pub unsafe fn opendir(dirname: *c_char) -> *DIR { + rust_opendir(dirname) + } + pub unsafe fn readdir(dirp: *DIR) -> *dirent_t { + rust_readdir(dirp) + } + + extern { + unsafe fn rust_opendir(dirname: *c_char) -> *DIR; + unsafe fn rust_readdir(dirp: *DIR) -> *dirent_t; + } + + pub extern { + unsafe fn closedir(dirp: *DIR) -> c_int; + unsafe fn rewinddir(dirp: *DIR); + unsafe fn seekdir(dirp: *DIR, loc: c_long); + unsafe fn telldir(dirp: *DIR) -> c_long; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod unistd { + use libc::types::common::c95::c_void; + use libc::types::os::arch::c95::{c_char, c_int, c_long, c_uint}; + use libc::types::os::arch::c95::{size_t}; + use libc::types::os::arch::posix88::{gid_t, off_t, pid_t}; + use libc::types::os::arch::posix88::{ssize_t, uid_t}; + + pub extern { + unsafe fn access(path: *c_char, amode: c_int) -> c_int; + unsafe fn alarm(seconds: c_uint) -> c_uint; + unsafe fn chdir(dir: *c_char) -> c_int; + unsafe fn chown(path: *c_char, uid: uid_t, gid: gid_t) + -> c_int; + unsafe fn close(fd: c_int) -> c_int; + unsafe fn dup(fd: c_int) -> c_int; + unsafe fn dup2(src: c_int, dst: c_int) -> c_int; + unsafe fn execv(prog: *c_char, argv: **c_char) -> c_int; + unsafe fn execve(prog: *c_char, + argv: **c_char, + envp: **c_char) + -> c_int; + unsafe fn execvp(c: *c_char, argv: **c_char) -> c_int; + unsafe fn fork() -> pid_t; + unsafe fn fpathconf(filedes: c_int, name: c_int) -> c_long; + unsafe fn getcwd(buf: *c_char, size: size_t) -> *c_char; + unsafe fn getegid() -> gid_t; + unsafe fn geteuid() -> uid_t; + unsafe fn getgid() -> gid_t ; + unsafe fn getgroups(ngroups_max: c_int, groups: *mut gid_t) + -> c_int; + unsafe fn getlogin() -> *c_char; + unsafe fn getopt(argc: c_int, argv: **c_char, optstr: *c_char) + -> c_int; + unsafe fn getpgrp() -> pid_t; + unsafe fn getpid() -> pid_t; + unsafe fn getppid() -> pid_t; + unsafe fn getuid() -> uid_t; + unsafe fn isatty(fd: c_int) -> c_int; + unsafe fn link(src: *c_char, dst: *c_char) -> c_int; + unsafe fn lseek(fd: c_int, offset: off_t, whence: c_int) + -> off_t; + unsafe fn pathconf(path: *c_char, name: c_int) -> c_long; + unsafe fn pause() -> c_int; + unsafe fn pipe(fds: *mut c_int) -> c_int; + #[fast_ffi] + unsafe fn read(fd: c_int, buf: *mut c_void, + count: size_t) -> ssize_t; + unsafe fn rmdir(path: *c_char) -> c_int; + unsafe fn setgid(gid: gid_t) -> c_int; + unsafe fn setpgid(pid: pid_t, pgid: pid_t) -> c_int; + unsafe fn setsid() -> pid_t; + unsafe fn setuid(uid: uid_t) -> c_int; + unsafe fn sleep(secs: c_uint) -> c_uint; + unsafe fn sysconf(name: c_int) -> c_long; + unsafe fn tcgetpgrp(fd: c_int) -> pid_t; + unsafe fn ttyname(fd: c_int) -> *c_char; + unsafe fn unlink(c: *c_char) -> c_int; + #[fast_ffi] + unsafe fn write(fd: c_int, buf: *c_void, count: size_t) + -> ssize_t; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod signal { + use libc::types::os::arch::c95::{c_int}; + use libc::types::os::arch::posix88::{pid_t}; + + pub extern { + unsafe fn kill(pid: pid_t, sig: c_int) -> c_int; + } + } + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + pub mod posix01 { + #[nolink] + #[abi = "cdecl"] + pub mod stat_ { + use libc::types::os::arch::c95::{c_char, c_int}; + use libc::types::os::arch::posix01::stat; + + pub extern { + #[cfg(target_os = "linux")] + #[cfg(target_os = "freebsd")] + #[cfg(target_os = "android")] + unsafe fn lstat(path: *c_char, buf: *mut stat) -> c_int; + + #[cfg(target_os = "macos")] + #[link_name = "lstat64"] + unsafe fn lstat(path: *c_char, buf: *mut stat) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod unistd { + use libc::types::os::arch::c95::{c_char, c_int, size_t}; + use libc::types::os::arch::posix88::{ssize_t}; + + pub extern { + unsafe fn readlink(path: *c_char, buf: *mut c_char, + bufsz: size_t) -> ssize_t; + + unsafe fn fsync(fd: c_int) -> c_int; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + unsafe fn fdatasync(fd: c_int) -> c_int; + + unsafe fn setenv(name: *c_char, val: *c_char, + overwrite: c_int) -> c_int; + unsafe fn unsetenv(name: *c_char) -> c_int; + unsafe fn putenv(string: *c_char) -> c_int; + + unsafe fn symlink(path1: *c_char, path2: *c_char) -> c_int; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod wait { + use libc::types::os::arch::c95::{c_int}; + use libc::types::os::arch::posix88::{pid_t}; + + pub extern { + unsafe fn waitpid(pid: pid_t, + status: *mut c_int, + options: c_int) + -> pid_t; + } + } + + #[nolink] + #[abi = "cdecl"] + pub mod glob { + use libc::types::common::c95::{c_void}; + use libc::types::os::arch::c95::{c_char, c_int}; + use libc::types::os::common::posix01::{glob_t}; + + pub extern { + unsafe fn glob(pattern: *c_char, flags: c_int, + errfunc: *c_void, // XXX callback + pglob: *mut glob_t); + unsafe fn globfree(pglob: *mut glob_t); + } + } + } + + #[cfg(target_os = "win32")] + pub mod posix01 { + pub mod stat_ { + } + + pub mod unistd { + } + + pub mod glob { + } + } + + + #[cfg(target_os = "win32")] + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + pub mod posix08 { + pub mod unistd { + } + } + + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + pub mod bsd44 { + use libc::types::common::c95::{c_void}; + use libc::types::os::arch::c95::{c_char, c_int, c_uint, size_t}; + + #[abi = "cdecl"] + pub extern { + unsafe fn sysctl(name: *c_int, namelen: c_uint, + oldp: *mut c_void, oldlenp: *mut size_t, + newp: *c_void, newlen: size_t) -> c_int; + + unsafe fn sysctlbyname(name: *c_char, + oldp: *mut c_void, oldlenp: *mut size_t, + newp: *c_void, newlen: size_t) -> c_int; + + unsafe fn sysctlnametomib(name: *c_char, mibp: *mut c_int, + sizep: *mut size_t) -> c_int; + + unsafe fn getdtablesize() -> c_int; + } + } + + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + pub mod bsd44 { + use libc::types::os::arch::c95::{c_int}; + + #[abi = "cdecl"] + pub extern { + unsafe fn getdtablesize() -> c_int; + } + } + + + #[cfg(target_os = "win32")] + pub mod bsd44 { + } + + #[cfg(target_os = "macos")] + #[nolink] + pub mod extra { + use libc::types::os::arch::c95::{c_char, c_int}; + + #[abi = "cdecl"] + pub extern { + unsafe fn _NSGetExecutablePath(buf: *mut c_char, + bufsize: *mut u32) + -> c_int; + } + } + + #[cfg(target_os = "freebsd")] + pub mod extra { + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + pub mod extra { + } + + + #[cfg(target_os = "win32")] + pub mod extra { + + pub mod kernel32 { + use libc::types::os::arch::c95::{c_uint}; + use libc::types::os::arch::extra::{BOOL, DWORD, HMODULE}; + use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPCTSTR, + LPTSTR, LPTCH, LPDWORD, LPVOID}; + use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, LPSTARTUPINFO, + LPPROCESS_INFORMATION}; + use libc::types::os::arch::extra::{HANDLE, LPHANDLE}; + + #[abi = "stdcall"] + pub extern "stdcall" { + unsafe fn GetEnvironmentVariableW(n: LPCWSTR, + v: LPWSTR, + nsize: DWORD) + -> DWORD; + unsafe fn SetEnvironmentVariableW(n: LPCWSTR, v: LPCWSTR) + -> BOOL; + unsafe fn GetEnvironmentStringsA() -> LPTCH; + unsafe fn FreeEnvironmentStringsA(env_ptr: LPTCH) -> BOOL; + + unsafe fn GetModuleFileNameW(hModule: HMODULE, + lpFilename: LPWSTR, + nSize: DWORD) + -> DWORD; + unsafe fn CreateDirectoryW(lpPathName: LPCWSTR, + lpSecurityAttributes: + LPSECURITY_ATTRIBUTES) + -> BOOL; + unsafe fn CopyFileW(lpExistingFileName: LPCWSTR, + lpNewFileName: LPCWSTR, + bFailIfExists: BOOL) + -> BOOL; + unsafe fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL; + unsafe fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL; + unsafe fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL; + + unsafe fn GetLastError() -> DWORD; + unsafe fn FindFirstFileW(fileName: *u16, + findFileData: HANDLE) + -> HANDLE; + unsafe fn FindNextFileW(findFile: HANDLE, + findFileData: HANDLE) + -> BOOL; + unsafe fn FindClose(findFile: HANDLE) -> BOOL; + unsafe fn DuplicateHandle(hSourceProcessHandle: HANDLE, + hSourceHandle: HANDLE, + hTargetProcessHandle: HANDLE, + lpTargetHandle: LPHANDLE, + dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwOptions: DWORD) -> BOOL; + unsafe fn CloseHandle(hObject: HANDLE) -> BOOL; + unsafe fn OpenProcess(dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwProcessId: DWORD) -> HANDLE; + unsafe fn GetCurrentProcess() -> HANDLE; + unsafe fn CreateProcessA(lpApplicationName: LPCTSTR, + lpCommandLine: LPTSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCTSTR, + lpStartupInfo: LPSTARTUPINFO, + lpProcessInformation: LPPROCESS_INFORMATION) -> BOOL; + unsafe fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + unsafe fn TerminateProcess(hProcess: HANDLE, uExitCode: c_uint) -> BOOL; + unsafe fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL; + } + } + + pub mod msvcrt { + use libc::types::os::arch::c95::{c_int, c_long}; + + #[abi = "cdecl"] + #[nolink] + pub extern { + #[link_name = "_commit"] + unsafe fn commit(fd: c_int) -> c_int; + + #[link_name = "_get_osfhandle"] + unsafe fn get_osfhandle(fd: c_int) -> c_long; + } + } + } +} diff --git a/src/libstd/list.rs b/src/libstd/list.rs deleted file mode 100644 index 3a916233db8..00000000000 --- a/src/libstd/list.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! A standard linked list - -#[deriving(Eq)] -pub enum List<T> { - Cons(T, @List<T>), - Nil, -} - -#[deriving(Eq)] -pub enum MutList<T> { - MutCons(T, @mut MutList<T>), - MutNil, -} - -/// Create a list from a vector -pub fn from_vec<T:Copy>(v: &[T]) -> @List<T> { - vec::foldr(v, @Nil::<T>, |h, t| @Cons(*h, t)) -} - -/** - * Left fold - * - * Applies `f` to `u` and the first element in the list, then applies `f` to - * the result of the previous call and the second element, and so on, - * returning the accumulated result. - * - * # Arguments - * - * * ls - The list to fold - * * z - The initial value - * * f - The function to apply - */ -pub fn foldl<T:Copy,U>(z: T, ls: @List<U>, f: &fn(&T, &U) -> T) -> T { - let mut accum: T = z; - do iter(ls) |elt| { accum = f(&accum, elt);} - accum -} - -/** - * Search for an element that matches a given predicate - * - * Apply function `f` to each element of `v`, starting from the first. - * When function `f` returns true then an option containing the element - * is returned. If `f` matches no elements then none is returned. - */ -pub fn find<T:Copy>(ls: @List<T>, f: &fn(&T) -> bool) -> Option<T> { - let mut ls = ls; - loop { - ls = match *ls { - Cons(ref hd, tl) => { - if f(hd) { return Some(*hd); } - tl - } - Nil => return None - } - }; -} - -/// Returns true if a list contains an element with the given value -pub fn has<T:Copy + Eq>(ls: @List<T>, elt: T) -> bool { - for each(ls) |e| { - if *e == elt { return true; } - } - return false; -} - -/// Returns true if the list is empty -pub fn is_empty<T:Copy>(ls: @List<T>) -> bool { - match *ls { - Nil => true, - _ => false - } -} - -/// Returns the length of a list -pub fn len<T>(ls: @List<T>) -> uint { - let mut count = 0u; - iter(ls, |_e| count += 1u); - count -} - -/// Returns all but the first element of a list -pub fn tail<T:Copy>(ls: @List<T>) -> @List<T> { - match *ls { - Cons(_, tl) => return tl, - Nil => fail!("list empty") - } -} - -/// Returns the first element of a list -pub fn head<T:Copy>(ls: @List<T>) -> T { - match *ls { - Cons(copy hd, _) => hd, - // makes me sad - _ => fail!("head invoked on empty list") - } -} - -/// Appends one list to another -pub fn append<T:Copy>(l: @List<T>, m: @List<T>) -> @List<T> { - match *l { - Nil => return m, - Cons(copy x, xs) => { - let rest = append(xs, m); - return @Cons(x, rest); - } - } -} - -/* -/// Push one element into the front of a list, returning a new list -/// THIS VERSION DOESN'T ACTUALLY WORK -fn push<T:Copy>(ll: &mut @list<T>, vv: T) { - ll = &mut @cons(vv, *ll) -} -*/ - -/// Iterate over a list -pub fn iter<T>(l: @List<T>, f: &fn(&T)) { - let mut cur = l; - loop { - cur = match *cur { - Cons(ref hd, tl) => { - f(hd); - tl - } - Nil => break - } - } -} - -/// Iterate over a list -pub fn each<T>(l: @List<T>, f: &fn(&T) -> bool) -> bool { - let mut cur = l; - loop { - cur = match *cur { - Cons(ref hd, tl) => { - if !f(hd) { return false; } - tl - } - Nil => { return true; } - } - } -} - -impl<T> MutList<T> { - /// Iterate over a mutable list - pub fn each(@mut self, f: &fn(&mut T) -> bool) -> bool { - let mut cur = self; - loop { - let borrowed = &mut *cur; - cur = match *borrowed { - MutCons(ref mut hd, tl) => { - if !f(hd) { - return false; - } - tl - } - MutNil => break - } - } - return true; - } -} - -#[cfg(test)] -mod tests { - use list::*; - use list; - - use core::option; - - #[test] - fn test_is_empty() { - let empty : @list::List<int> = from_vec(~[]); - let full1 = from_vec(~[1]); - let full2 = from_vec(~['r', 'u']); - - assert!(is_empty(empty)); - assert!(!is_empty(full1)); - assert!(!is_empty(full2)); - } - - #[test] - fn test_from_vec() { - let l = from_vec(~[0, 1, 2]); - - assert_eq!(head(l), 0); - - let tail_l = tail(l); - assert_eq!(head(tail_l), 1); - - let tail_tail_l = tail(tail_l); - assert_eq!(head(tail_tail_l), 2); - } - - #[test] - fn test_from_vec_empty() { - let empty : @list::List<int> = from_vec(~[]); - assert_eq!(empty, @list::Nil::<int>); - } - - #[test] - fn test_foldl() { - fn add(a: &uint, b: &int) -> uint { return *a + (*b as uint); } - let l = from_vec(~[0, 1, 2, 3, 4]); - let empty = @list::Nil::<int>; - assert_eq!(list::foldl(0u, l, add), 10u); - assert_eq!(list::foldl(0u, empty, add), 0u); - } - - #[test] - fn test_foldl2() { - fn sub(a: &int, b: &int) -> int { - *a - *b - } - let l = from_vec(~[1, 2, 3, 4]); - assert_eq!(list::foldl(0, l, sub), -10); - } - - #[test] - fn test_find_success() { - fn match_(i: &int) -> bool { return *i == 2; } - let l = from_vec(~[0, 1, 2]); - assert_eq!(list::find(l, match_), option::Some(2)); - } - - #[test] - fn test_find_fail() { - fn match_(_i: &int) -> bool { return false; } - let l = from_vec(~[0, 1, 2]); - let empty = @list::Nil::<int>; - assert_eq!(list::find(l, match_), option::None::<int>); - assert_eq!(list::find(empty, match_), option::None::<int>); - } - - #[test] - fn test_has() { - let l = from_vec(~[5, 8, 6]); - let empty = @list::Nil::<int>; - assert!((list::has(l, 5))); - assert!((!list::has(l, 7))); - assert!((list::has(l, 8))); - assert!((!list::has(empty, 5))); - } - - #[test] - fn test_len() { - let l = from_vec(~[0, 1, 2]); - let empty = @list::Nil::<int>; - assert_eq!(list::len(l), 3u); - assert_eq!(list::len(empty), 0u); - } - - #[test] - fn test_append() { - assert!(from_vec(~[1,2,3,4]) - == list::append(list::from_vec(~[1,2]), list::from_vec(~[3,4]))); - } -} diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs new file mode 100644 index 00000000000..2cbf8b9f05e --- /dev/null +++ b/src/libstd/local_data.rs @@ -0,0 +1,226 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Task local data management + +Allows storing boxes with arbitrary types inside, to be accessed +anywhere within a task, keyed by a pointer to a global finaliser +function. Useful for dynamic variables, singletons, and interfacing +with foreign code with bad callback interfaces. + +To use, declare a monomorphic global function at the type to store, +and use it as the 'key' when accessing. See the 'tls' tests below for +examples. + +Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation +magic. + +*/ + +use prelude::*; +use task::local_data_priv::{local_get, local_pop, local_modify, local_set, Handle}; + +/** + * Indexes a task-local data slot. The function's code pointer is used for + * comparison. Recommended use is to write an empty function for each desired + * task-local data slot (and use class destructors, not code inside the + * function, if specific teardown is needed). DO NOT use multiple + * instantiations of a single polymorphic function to index data of different + * types; arbitrary type coercion is possible this way. + * + * One other exception is that this global state can be used in a destructor + * context to create a circular @-box reference, which will crash during task + * failure (see issue #3039). + * + * These two cases aside, the interface is safe. + */ +pub type LocalDataKey<'self,T> = &'self fn(v: @T); + +/** + * Remove a task-local data value from the table, returning the + * reference that was originally created to insert it. + */ +pub unsafe fn local_data_pop<T: 'static>( + key: LocalDataKey<T>) -> Option<@T> { + + local_pop(Handle::new(), key) +} +/** + * Retrieve a task-local data value. It will also be kept alive in the + * table until explicitly removed. + */ +pub unsafe fn local_data_get<T: 'static>( + key: LocalDataKey<T>) -> Option<@T> { + + local_get(Handle::new(), key) +} +/** + * Store a value in task-local data. If this key already has a value, + * that value is overwritten (and its destructor is run). + */ +pub unsafe fn local_data_set<T: 'static>( + key: LocalDataKey<T>, data: @T) { + + local_set(Handle::new(), key, data) +} +/** + * Modify a task-local data value. If the function returns 'None', the + * data is removed (and its reference dropped). + */ +pub unsafe fn local_data_modify<T: 'static>( + key: LocalDataKey<T>, + modify_fn: &fn(Option<@T>) -> Option<@T>) { + + local_modify(Handle::new(), key, modify_fn) +} + +#[test] +fn test_tls_multitask() { + unsafe { + fn my_key(_x: @~str) { } + local_data_set(my_key, @~"parent data"); + do task::spawn { + unsafe { + // TLS shouldn't carry over. + assert!(local_data_get(my_key).is_none()); + local_data_set(my_key, @~"child data"); + assert!(*(local_data_get(my_key).get()) == + ~"child data"); + // should be cleaned up for us + } + } + // Must work multiple times + assert!(*(local_data_get(my_key).get()) == ~"parent data"); + assert!(*(local_data_get(my_key).get()) == ~"parent data"); + assert!(*(local_data_get(my_key).get()) == ~"parent data"); + } +} + +#[test] +fn test_tls_overwrite() { + unsafe { + fn my_key(_x: @~str) { } + local_data_set(my_key, @~"first data"); + local_data_set(my_key, @~"next data"); // Shouldn't leak. + assert!(*(local_data_get(my_key).get()) == ~"next data"); + } +} + +#[test] +fn test_tls_pop() { + unsafe { + fn my_key(_x: @~str) { } + local_data_set(my_key, @~"weasel"); + assert!(*(local_data_pop(my_key).get()) == ~"weasel"); + // Pop must remove the data from the map. + assert!(local_data_pop(my_key).is_none()); + } +} + +#[test] +fn test_tls_modify() { + unsafe { + fn my_key(_x: @~str) { } + local_data_modify(my_key, |data| { + match data { + Some(@ref val) => fail!("unwelcome value: %s", *val), + None => Some(@~"first data") + } + }); + local_data_modify(my_key, |data| { + match data { + Some(@~"first data") => Some(@~"next data"), + Some(@ref val) => fail!("wrong value: %s", *val), + None => fail!("missing value") + } + }); + assert!(*(local_data_pop(my_key).get()) == ~"next data"); + } +} + +#[test] +fn test_tls_crust_automorestack_memorial_bug() { + // This might result in a stack-canary clobber if the runtime fails to + // set sp_limit to 0 when calling the cleanup extern - it might + // automatically jump over to the rust stack, which causes next_c_sp + // to get recorded as something within a rust stack segment. Then a + // subsequent upcall (esp. for logging, think vsnprintf) would run on + // a stack smaller than 1 MB. + fn my_key(_x: @~str) { } + do task::spawn { + unsafe { local_data_set(my_key, @~"hax"); } + } +} + +#[test] +fn test_tls_multiple_types() { + fn str_key(_x: @~str) { } + fn box_key(_x: @@()) { } + fn int_key(_x: @int) { } + do task::spawn { + unsafe { + local_data_set(str_key, @~"string data"); + local_data_set(box_key, @@()); + local_data_set(int_key, @42); + } + } +} + +#[test] +fn test_tls_overwrite_multiple_types() { + fn str_key(_x: @~str) { } + fn box_key(_x: @@()) { } + fn int_key(_x: @int) { } + do task::spawn { + unsafe { + local_data_set(str_key, @~"string data"); + local_data_set(int_key, @42); + // This could cause a segfault if overwriting-destruction is done + // with the crazy polymorphic transmute rather than the provided + // finaliser. + local_data_set(int_key, @31337); + } + } +} + +#[test] +#[should_fail] +#[ignore(cfg(windows))] +fn test_tls_cleanup_on_failure() { + unsafe { + fn str_key(_x: @~str) { } + fn box_key(_x: @@()) { } + fn int_key(_x: @int) { } + local_data_set(str_key, @~"parent data"); + local_data_set(box_key, @@()); + do task::spawn { + unsafe { // spawn_linked + local_data_set(str_key, @~"string data"); + local_data_set(box_key, @@()); + local_data_set(int_key, @42); + fail!(); + } + } + // Not quite nondeterministic. + local_data_set(int_key, @31337); + fail!(); + } +} + +#[test] +fn test_static_pointer() { + unsafe { + fn key(_x: @&'static int) { } + static VALUE: int = 0; + local_data_set(key, @&VALUE); + } +} diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs new file mode 100644 index 00000000000..be71714a048 --- /dev/null +++ b/src/libstd/logging.rs @@ -0,0 +1,97 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Logging + +use option::*; +use either::*; +use rt; +use rt::logging::{Logger, StdErrLogger}; +use cast; +use str; + +/// Turns on logging to stdout globally +pub fn console_on() { + unsafe { + rustrt::rust_log_console_on(); + } +} + +/** + * Turns off logging to stdout globally + * + * Turns off the console unless the user has overridden the + * runtime environment's logging spec, e.g. by setting + * the RUST_LOG environment variable + */ +pub fn console_off() { + unsafe { + rustrt::rust_log_console_off(); + } +} + +#[cfg(not(test))] +#[lang="log_type"] +pub fn log_type<T>(level: u32, object: &T) { + use container::Container; + use io; + use libc; + use repr; + use vec; + + let bytes = do io::with_bytes_writer |writer| { + repr::write_repr(writer, object); + }; + + match rt::context() { + rt::OldTaskContext => { + unsafe { + let len = bytes.len() as libc::size_t; + rustrt::rust_log_str(level, cast::transmute(vec::raw::to_ptr(bytes)), len); + } + } + _ => { + // XXX: Bad allocation + let msg = str::from_bytes(bytes); + newsched_log_str(msg); + } + } +} + +fn newsched_log_str(msg: ~str) { + use rt::task::Task; + use rt::local::Local; + + unsafe { + match Local::try_unsafe_borrow::<Task>() { + Some(local) => { + // Use the available logger + (*local).logger.log(Left(msg)); + } + None => { + // There is no logger anywhere, just write to stderr + let mut logger = StdErrLogger; + logger.log(Left(msg)); + } + } + } +} + +pub mod rustrt { + use libc; + + pub extern { + unsafe fn rust_log_console_on(); + unsafe fn rust_log_console_off(); + unsafe fn rust_log_str(level: u32, + string: *libc::c_char, + size: libc::size_t); + } +} diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs new file mode 100644 index 00000000000..fda48b6ffb7 --- /dev/null +++ b/src/libstd/macros.rs @@ -0,0 +1,53 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[macro_escape]; + +// Some basic logging +macro_rules! rtdebug_ ( + ($( $arg:expr),+) => ( { + dumb_println(fmt!( $($arg),+ )); + + fn dumb_println(s: &str) { + use io::WriterUtil; + let dbg = ::libc::STDERR_FILENO as ::io::fd_t; + dbg.write_str(s); + dbg.write_str("\n"); + } + + } ) +) + +// An alternate version with no output, for turning off logging +macro_rules! rtdebug ( + ($( $arg:expr),+) => ( $(let _ = $arg)*; ) +) + +macro_rules! rtassert ( + ( $arg:expr ) => ( { + if !$arg { + abort!("assertion failed: %s", stringify!($arg)); + } + } ) +) + +macro_rules! abort( + ($( $msg:expr),+) => ( { + rtdebug!($($msg),+); + + do_abort(); + + // NB: This is in a fn to avoid putting the `unsafe` block in a macro, + // which causes spurious 'unnecessary unsafe block' warnings. + fn do_abort() -> ! { + unsafe { ::libc::abort(); } + } + } ) +) diff --git a/src/libstd/managed.rs b/src/libstd/managed.rs new file mode 100644 index 00000000000..ecde1eb1917 --- /dev/null +++ b/src/libstd/managed.rs @@ -0,0 +1,100 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations on managed box types + +use ptr::to_unsafe_ptr; + +#[cfg(not(test))] use cmp::{Eq, Ord}; + +pub mod raw { + use intrinsic::TyDesc; + + pub static RC_EXCHANGE_UNIQUE : uint = (-1) as uint; + pub static RC_MANAGED_UNIQUE : uint = (-2) as uint; + pub static RC_IMMORTAL : uint = 0x77777777; + + pub struct BoxHeaderRepr { + ref_count: uint, + type_desc: *TyDesc, + prev: *BoxRepr, + next: *BoxRepr, + } + + pub struct BoxRepr { + header: BoxHeaderRepr, + data: u8 + } + +} + +/// Determine if two shared boxes point to the same object +#[inline(always)] +pub fn ptr_eq<T>(a: @T, b: @T) -> bool { + let a_ptr: *T = to_unsafe_ptr(&*a), b_ptr: *T = to_unsafe_ptr(&*b); + a_ptr == b_ptr +} + +/// Determine if two mutable shared boxes point to the same object +#[inline(always)] +pub fn mut_ptr_eq<T>(a: @mut T, b: @mut T) -> bool { + let a_ptr: *T = to_unsafe_ptr(&*a), b_ptr: *T = to_unsafe_ptr(&*b); + a_ptr == b_ptr +} + +#[cfg(not(test))] +impl<T:Eq> Eq for @T { + #[inline(always)] + fn eq(&self, other: &@T) -> bool { *(*self) == *(*other) } + #[inline(always)] + fn ne(&self, other: &@T) -> bool { *(*self) != *(*other) } +} + +#[cfg(not(test))] +impl<T:Eq> Eq for @mut T { + #[inline(always)] + fn eq(&self, other: &@mut T) -> bool { *(*self) == *(*other) } + #[inline(always)] + fn ne(&self, other: &@mut T) -> bool { *(*self) != *(*other) } +} + +#[cfg(not(test))] +impl<T:Ord> Ord for @T { + #[inline(always)] + fn lt(&self, other: &@T) -> bool { *(*self) < *(*other) } + #[inline(always)] + fn le(&self, other: &@T) -> bool { *(*self) <= *(*other) } + #[inline(always)] + fn ge(&self, other: &@T) -> bool { *(*self) >= *(*other) } + #[inline(always)] + fn gt(&self, other: &@T) -> bool { *(*self) > *(*other) } +} + +#[cfg(not(test))] +impl<T:Ord> Ord for @mut T { + #[inline(always)] + fn lt(&self, other: &@mut T) -> bool { *(*self) < *(*other) } + #[inline(always)] + fn le(&self, other: &@mut T) -> bool { *(*self) <= *(*other) } + #[inline(always)] + fn ge(&self, other: &@mut T) -> bool { *(*self) >= *(*other) } + #[inline(always)] + fn gt(&self, other: &@mut T) -> bool { *(*self) > *(*other) } +} + +#[test] +fn test() { + let x = @3; + let y = @3; + assert!((ptr_eq::<int>(x, x))); + assert!((ptr_eq::<int>(y, y))); + assert!((!ptr_eq::<int>(x, y))); + assert!((!ptr_eq::<int>(y, x))); +} diff --git a/src/libstd/md4.rs b/src/libstd/md4.rs deleted file mode 100644 index da81f730eda..00000000000 --- a/src/libstd/md4.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 core::str; -use core::uint; -use core::vec; - -struct Quad { - a: u32, - b: u32, - c: u32, - d: u32 -} - -pub fn md4(msg: &[u8]) -> Quad { - // subtle: if orig_len is merely uint, then the code below - // which performs shifts by 32 bits or more has undefined - // results. - let orig_len: u64 = (msg.len() * 8u) as u64; - - // pad message - let mut msg = vec::append(vec::to_owned(msg), [0x80u8]); - let mut bitlen = orig_len + 8u64; - while (bitlen + 64u64) % 512u64 > 0u64 { - msg.push(0u8); - bitlen += 8u64; - } - - // append length - let mut i = 0u64; - while i < 8u64 { - msg.push((orig_len >> (i * 8u64)) as u8); - i += 1u64; - } - - let mut a = 0x67452301u32; - let mut b = 0xefcdab89u32; - let mut c = 0x98badcfeu32; - let mut d = 0x10325476u32; - - fn rot(r: int, x: u32) -> u32 { - let r = r as u32; - (x << r) | (x >> (32u32 - r)) - } - - let mut i = 0u; - let e = msg.len(); - let mut x = vec::from_elem(16u, 0u32); - while i < e { - let aa = a, bb = b, cc = c, dd = d; - - let mut j = 0u, base = i; - while j < 16u { - x[j] = (msg[base] as u32) + (msg[base + 1u] as u32 << 8u32) + - (msg[base + 2u] as u32 << 16u32) + - (msg[base + 3u] as u32 << 24u32); - j += 1u; base += 4u; - } - - let mut j = 0u; - while j < 16u { - a = rot(3, a + ((b & c) | (!b & d)) + x[j]); - j += 1u; - d = rot(7, d + ((a & b) | (!a & c)) + x[j]); - j += 1u; - c = rot(11, c + ((d & a) | (!d & b)) + x[j]); - j += 1u; - b = rot(19, b + ((c & d) | (!c & a)) + x[j]); - j += 1u; - } - - let mut j = 0u; - let q = 0x5a827999u32; - while j < 4u { - a = rot(3, a + ((b & c) | ((b & d) | (c & d))) + x[j] + q); - d = rot(5, d + ((a & b) | ((a & c) | (b & c))) + x[j + 4u] + q); - c = rot(9, c + ((d & a) | ((d & b) | (a & b))) + x[j + 8u] + q); - b = rot(13, b + ((c & d) | ((c & a) | (d & a))) + x[j + 12u] + q); - j += 1u; - } - - let mut j = 0u; - let q = 0x6ed9eba1u32; - while j < 8u { - let jj = if j > 2u { j - 3u } else { j }; - a = rot(3, a + (b ^ c ^ d) + x[jj] + q); - d = rot(9, d + (a ^ b ^ c) + x[jj + 8u] + q); - c = rot(11, c + (d ^ a ^ b) + x[jj + 4u] + q); - b = rot(15, b + (c ^ d ^ a) + x[jj + 12u] + q); - j += 2u; - } - - a += aa; b += bb; c += cc; d += dd; - i += 64u; - } - return Quad {a: a, b: b, c: c, d: d}; -} - -pub fn md4_str(msg: &[u8]) -> ~str { - let Quad {a, b, c, d} = md4(msg); - fn app(a: u32, b: u32, c: u32, d: u32, f: &fn(u32)) { - f(a); f(b); f(c); f(d); - } - let mut result = ~""; - do app(a, b, c, d) |u| { - let mut i = 0u32; - while i < 4u32 { - let byte = (u >> (i * 8u32)) as u8; - if byte <= 16u8 { result += ~"0"; } - result += uint::to_str_radix(byte as uint, 16u); - i += 1u32; - } - } - result -} - -pub fn md4_text(msg: &str) -> ~str { md4_str(str::to_bytes(msg)) } - -#[test] -fn test_md4() { - assert_eq!(md4_text(~""), ~"31d6cfe0d16ae931b73c59d7e0c089c0"); - assert_eq!(md4_text(~"a"), ~"bde52cb31de33e46245e05fbdbd6fb24"); - assert_eq!(md4_text(~"abc"), ~"a448017aaf21d8525fc10ae87aa6729d"); - assert!(md4_text(~"message digest") == - ~"d9130a8164549fe818874806e1c7014b"); - assert!(md4_text(~"abcdefghijklmnopqrstuvwxyz") == - ~"d79e1c308aa5bbcdeea8ed63df412da9"); - assert!(md4_text( - ~"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ - 0123456789") == ~"043f8582f241db351ce627e153e7f0e4"); - assert!(md4_text(~"1234567890123456789012345678901234567890123456789\ - 0123456789012345678901234567890") == - ~"e33b4ddc9c38f2199c3e7b164fcc0536"); -} diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs deleted file mode 100644 index 4f67f1c5f13..00000000000 --- a/src/libstd/net_ip.rs +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Types/fns concerning Internet Protocol (IP), versions 4 & 6 - -use core::libc; -use core::comm::{stream, SharedChan}; -use core::ptr; -use core::result; -use core::str; - -use iotask = uv::iotask::IoTask; -use interact = uv::iotask::interact; - -use sockaddr_in = super::uv_ll::sockaddr_in; -use sockaddr_in6 = super::uv_ll::sockaddr_in6; -use addrinfo = super::uv_ll::addrinfo; -use uv_getaddrinfo_t = super::uv_ll::uv_getaddrinfo_t; -use uv_ip4_name = super::uv_ll::ip4_name; -use uv_ip4_port = super::uv_ll::ip4_port; -use uv_ip6_name = super::uv_ll::ip6_name; -use uv_ip6_port = super::uv_ll::ip6_port; -use uv_getaddrinfo = super::uv_ll::getaddrinfo; -use uv_freeaddrinfo = super::uv_ll::freeaddrinfo; -use create_uv_getaddrinfo_t = super::uv_ll::getaddrinfo_t; -use set_data_for_req = super::uv_ll::set_data_for_req; -use get_data_for_req = super::uv_ll::get_data_for_req; -use ll = super::uv_ll; - -/// An IP address -pub enum IpAddr { - /// An IPv4 address - Ipv4(sockaddr_in), - Ipv6(sockaddr_in6) -} - -/// Human-friendly feedback on why a parse_addr attempt failed -pub struct ParseAddrErr { - err_msg: ~str, -} - -/** - * Convert a `IpAddr` to a str - * - * # Arguments - * - * * ip - a `std::net::ip::IpAddr` - */ -pub fn format_addr(ip: &IpAddr) -> ~str { - match *ip { - Ipv4(ref addr) => unsafe { - let result = uv_ip4_name(addr); - if result == ~"" { - fail!("failed to convert inner sockaddr_in address to str") - } - result - }, - Ipv6(ref addr) => unsafe { - let result = uv_ip6_name(addr); - if result == ~"" { - fail!("failed to convert inner sockaddr_in address to str") - } - result - } - } -} - -/** - * Get the associated port - * - * # Arguments - * * ip - a `std::net::ip::IpAddr` - */ -pub fn get_port(ip: &IpAddr) -> uint { - match *ip { - Ipv4(ref addr) => unsafe { - uv_ip4_port(addr) - }, - Ipv6(ref addr) => unsafe { - uv_ip6_port(addr) - } - } -} - -/// Represents errors returned from `net::ip::get_addr()` -enum IpGetAddrErr { - GetAddrUnknownError -} - -/** - * Attempts name resolution on the provided `node` string - * - * # Arguments - * - * * `node` - a string representing some host address - * * `iotask` - a `uv::iotask` used to interact with the underlying event loop - * - * # Returns - * - * A `result<~[ip_addr], ip_get_addr_err>` instance that will contain - * a vector of `ip_addr` results, in the case of success, or an error - * object in the case of failure -*/ -pub fn get_addr(node: &str, iotask: &iotask) - -> result::Result<~[IpAddr], IpGetAddrErr> { - let (output_po, output_ch) = stream(); - let mut output_ch = Some(SharedChan::new(output_ch)); - do str::as_buf(node) |node_ptr, len| { - let output_ch = output_ch.swap_unwrap(); - debug!("slice len %?", len); - let handle = create_uv_getaddrinfo_t(); - let handle_ptr: *uv_getaddrinfo_t = &handle; - let handle_data = GetAddrData { - output_ch: output_ch.clone() - }; - let handle_data_ptr: *GetAddrData = &handle_data; - do interact(iotask) |loop_ptr| { - unsafe { - let result = uv_getaddrinfo( - loop_ptr, - handle_ptr, - get_addr_cb, - node_ptr, - ptr::null(), - ptr::null()); - match result { - 0i32 => { - set_data_for_req(handle_ptr, handle_data_ptr); - } - _ => { - output_ch.send(result::Err(GetAddrUnknownError)); - } - } - } - }; - output_po.recv() - } -} - -pub mod v4 { - use net::ip::{IpAddr, Ipv4, ParseAddrErr}; - use uv::ll; - use uv_ip4_addr = uv::ll::ip4_addr; - use uv_ip4_name = uv::ll::ip4_name; - - use core::cast::transmute; - use core::result; - use core::str; - use core::uint; - use core::vec; - - /** - * Convert a str to `ip_addr` - * - * # Failure - * - * Fails if the string is not a valid IPv4 address - * - * # Arguments - * - * * ip - a string of the format `x.x.x.x` - * - * # Returns - * - * * an `ip_addr` of the `ipv4` variant - */ - pub fn parse_addr(ip: &str) -> IpAddr { - match try_parse_addr(ip) { - result::Ok(addr) => addr, - result::Err(ref err_data) => fail!(copy err_data.err_msg) - } - } - // the simple, old style numberic representation of - // ipv4 - pub struct Ipv4Rep { a: u8, b: u8, c: u8, d: u8 } - - pub trait AsUnsafeU32 { - unsafe fn as_u32(&self) -> u32; - } - - impl AsUnsafeU32 for Ipv4Rep { - // this is pretty dastardly, i know - unsafe fn as_u32(&self) -> u32 { - let this: &mut u32 = transmute(self); - *this - } - } - pub fn parse_to_ipv4_rep(ip: &str) -> result::Result<Ipv4Rep, ~str> { - let mut parts = ~[]; - for str::each_split_char(ip, '.') |s| { parts.push(s.to_owned()) } - let parts = vec::map(parts, |s| { - match uint::from_str(*s) { - Some(n) if n <= 255 => n, - _ => 256 - } - }); - if parts.len() != 4 { - Err(fmt!("'%s' doesn't have 4 parts", ip)) - } else if parts.contains(&256) { - Err(fmt!("invalid octal in addr '%s'", ip)) - } else { - Ok(Ipv4Rep { - a: parts[0] as u8, b: parts[1] as u8, - c: parts[2] as u8, d: parts[3] as u8, - }) - } - } - pub fn try_parse_addr(ip: &str) -> result::Result<IpAddr,ParseAddrErr> { - unsafe { - let INADDR_NONE = ll::get_INADDR_NONE(); - let ip_rep_result = parse_to_ipv4_rep(ip); - if result::is_err(&ip_rep_result) { - let err_str = result::get_err(&ip_rep_result); - return result::Err(ParseAddrErr { err_msg: err_str }) - } - // ipv4_rep.as_u32 is unsafe :/ - let input_is_inaddr_none = - result::get(&ip_rep_result).as_u32() == INADDR_NONE; - - let new_addr = uv_ip4_addr(str::to_owned(ip), 22); - let reformatted_name = uv_ip4_name(&new_addr); - debug!("try_parse_addr: input ip: %s reparsed ip: %s", - ip, reformatted_name); - let ref_ip_rep_result = parse_to_ipv4_rep(reformatted_name); - if result::is_err(&ref_ip_rep_result) { - let err_str = result::get_err(&ref_ip_rep_result); - return Err(ParseAddrErr { err_msg: err_str }) - } - - if result::get(&ref_ip_rep_result).as_u32() == INADDR_NONE && - !input_is_inaddr_none { - Err(ParseAddrErr { - err_msg: ~"uv_ip4_name produced invalid result.", - }) - } else { - Ok(Ipv4(copy(new_addr))) - } - } - } -} -pub mod v6 { - use net::ip::{IpAddr, Ipv6, ParseAddrErr}; - use uv_ip6_addr = uv::ll::ip6_addr; - use uv_ip6_name = uv::ll::ip6_name; - - use core::result; - use core::str; - - /** - * Convert a str to `ip_addr` - * - * # Failure - * - * Fails if the string is not a valid IPv6 address - * - * # Arguments - * - * * ip - an ipv6 string. See RFC2460 for spec. - * - * # Returns - * - * * an `ip_addr` of the `ipv6` variant - */ - pub fn parse_addr(ip: &str) -> IpAddr { - match try_parse_addr(ip) { - result::Ok(addr) => addr, - result::Err(copy err_data) => fail!(copy err_data.err_msg) - } - } - pub fn try_parse_addr(ip: &str) -> result::Result<IpAddr,ParseAddrErr> { - unsafe { - // need to figure out how to establish a parse failure.. - let new_addr = uv_ip6_addr(str::to_owned(ip), 22); - let reparsed_name = uv_ip6_name(&new_addr); - debug!("v6::try_parse_addr ip: '%s' reparsed '%s'", - ip, reparsed_name); - // '::' appears to be uv_ip6_name() returns for bogus - // parses.. - if ip != &"::" && reparsed_name == ~"::" { - Err(ParseAddrErr { err_msg:fmt!("failed to parse '%s'", ip) }) - } - else { - Ok(Ipv6(new_addr)) - } - } - } -} - -struct GetAddrData { - output_ch: SharedChan<result::Result<~[IpAddr],IpGetAddrErr>> -} - -extern fn get_addr_cb(handle: *uv_getaddrinfo_t, - status: libc::c_int, - res: *addrinfo) { - unsafe { - debug!("in get_addr_cb"); - let handle_data = get_data_for_req(handle) as - *GetAddrData; - let output_ch = (*handle_data).output_ch.clone(); - if status == 0i32 { - if res != (ptr::null::<addrinfo>()) { - let mut out_vec = ~[]; - debug!("initial addrinfo: %?", res); - let mut curr_addr = res; - loop { - let new_ip_addr = if ll::is_ipv4_addrinfo(curr_addr) { - Ipv4(copy(( - *ll::addrinfo_as_sockaddr_in(curr_addr)))) - } - else if ll::is_ipv6_addrinfo(curr_addr) { - Ipv6(copy(( - *ll::addrinfo_as_sockaddr_in6(curr_addr)))) - } - else { - debug!("curr_addr is not of family AF_INET or \ - AF_INET6. Error."); - output_ch.send( - result::Err(GetAddrUnknownError)); - break; - }; - out_vec.push(new_ip_addr); - - let next_addr = ll::get_next_addrinfo(curr_addr); - if next_addr == ptr::null::<addrinfo>() as *addrinfo { - debug!("null next_addr encountered. no mas"); - break; - } - else { - curr_addr = next_addr; - debug!("next_addr addrinfo: %?", curr_addr); - } - } - debug!("successful process addrinfo result, len: %?", - out_vec.len()); - output_ch.send(result::Ok(out_vec)); - } - else { - debug!("addrinfo pointer is NULL"); - output_ch.send( - result::Err(GetAddrUnknownError)); - } - } - else { - debug!("status != 0 error in get_addr_cb"); - output_ch.send( - result::Err(GetAddrUnknownError)); - } - if res != (ptr::null::<addrinfo>()) { - uv_freeaddrinfo(res); - } - debug!("leaving get_addr_cb"); - } -} - -#[cfg(test)] -mod test { - - use net_ip::*; - use net_ip::v4; - use net_ip::v6; - use uv; - - use core::result; - use core::vec; - - #[test] - fn test_ip_ipv4_parse_and_format_ip() { - let localhost_str = ~"127.0.0.1"; - assert!(format_addr(&v4::parse_addr(localhost_str)) - == localhost_str) - } - #[test] - fn test_ip_ipv6_parse_and_format_ip() { - let localhost_str = ~"::1"; - let format_result = format_addr(&v6::parse_addr(localhost_str)); - debug!("results: expected: '%s' actual: '%s'", - localhost_str, format_result); - assert_eq!(format_result, localhost_str); - } - #[test] - fn test_ip_ipv4_bad_parse() { - match v4::try_parse_addr(~"b4df00d") { - result::Err(ref err_info) => { - debug!("got error as expected %?", err_info); - assert!(true); - } - result::Ok(ref addr) => { - fail!("Expected failure, but got addr %?", addr); - } - } - } - #[test] - #[ignore(target_os="win32")] - fn test_ip_ipv6_bad_parse() { - match v6::try_parse_addr(~"::,~2234k;") { - result::Err(ref err_info) => { - debug!("got error as expected %?", err_info); - assert!(true); - } - result::Ok(ref addr) => { - fail!("Expected failure, but got addr %?", addr); - } - } - } - #[test] - #[ignore(reason = "valgrind says it's leaky")] - fn test_ip_get_addr() { - let localhost_name = ~"localhost"; - let iotask = &uv::global_loop::get(); - let ga_result = get_addr(localhost_name, iotask); - if result::is_err(&ga_result) { - fail!("got err result from net::ip::get_addr();") - } - // note really sure how to reliably test/assert - // this.. mostly just wanting to see it work, atm. - let results = result::unwrap(ga_result); - debug!("test_get_addr: Number of results for %s: %?", - localhost_name, results.len()); - for results.each |r| { - let ipv_prefix = match *r { - Ipv4(_) => ~"IPv4", - Ipv6(_) => ~"IPv6" - }; - debug!("test_get_addr: result %s: '%s'", - ipv_prefix, format_addr(r)); - } - // at least one result.. this is going to vary from system - // to system, based on stuff like the contents of /etc/hosts - assert!(!results.is_empty()); - } - #[test] - #[ignore(reason = "valgrind says it's leaky")] - fn test_ip_get_addr_bad_input() { - let localhost_name = ~"sjkl234m,./sdf"; - let iotask = &uv::global_loop::get(); - let ga_result = get_addr(localhost_name, iotask); - assert!(result::is_err(&ga_result)); - } -} diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs deleted file mode 100644 index db61679890b..00000000000 --- a/src/libstd/net_tcp.rs +++ /dev/null @@ -1,1970 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! High-level interface to libuv's TCP functionality -// FIXME #4425: Need FFI fixes - -use future; -use future_spawn = future::spawn; -use ip = net_ip; -use uv; -use uv::iotask; -use uv::iotask::IoTask; - -use core::io; -use core::libc::size_t; -use core::libc; -use core::comm::{stream, Port, SharedChan}; -use core::ptr; -use core::result::{Result}; -use core::result; -use core::uint; -use core::vec; - -pub mod rustrt { - use core::libc; - - #[nolink] - pub extern { - unsafe fn rust_uv_current_kernel_malloc(size: libc::c_uint) - -> *libc::c_void; - unsafe fn rust_uv_current_kernel_free(mem: *libc::c_void); - unsafe fn rust_uv_helper_uv_tcp_t_size() -> libc::c_uint; - } -} - -/** - * Encapsulates an open TCP/IP connection through libuv - * - * `TcpSocket` is non-copyable/sendable and automagically handles closing the - * underlying libuv data structures when it goes out of scope. This is the - * data structure that is used for read/write operations over a TCP stream. - */ -pub struct TcpSocket { - socket_data: @TcpSocketData, -} - -#[unsafe_destructor] -impl Drop for TcpSocket { - fn finalize(&self) { - tear_down_socket_data(self.socket_data) - } -} - -pub fn TcpSocket(socket_data: @TcpSocketData) -> TcpSocket { - TcpSocket { - socket_data: socket_data - } -} - -/** - * A buffered wrapper for `net::tcp::TcpSocket` - * - * It is created with a call to `net::tcp::socket_buf()` and has impls that - * satisfy both the `io::Reader` and `io::Writer` traits. - */ -pub struct TcpSocketBuf { - data: @mut TcpBufferedSocketData, - end_of_stream: @mut bool -} - -pub fn TcpSocketBuf(data: @mut TcpBufferedSocketData) -> TcpSocketBuf { - TcpSocketBuf { - data: data, - end_of_stream: @mut false - } -} - -/// Contains raw, string-based, error information returned from libuv -pub struct TcpErrData { - err_name: ~str, - err_msg: ~str, -} - -/// Details returned as part of a `Result::Err` result from `tcp::listen` -pub enum TcpListenErrData { - /** - * Some unplanned-for error. The first and second fields correspond - * to libuv's `err_name` and `err_msg` fields, respectively. - */ - GenericListenErr(~str, ~str), - /** - * Failed to bind to the requested IP/Port, because it is already in use. - * - * # Possible Causes - * - * * Attempting to bind to a port already bound to another listener - */ - AddressInUse, - /** - * Request to bind to an IP/Port was denied by the system. - * - * # Possible Causes - * - * * Attemping to binding to an IP/Port as a non-Administrator - * on Windows Vista+ - * * Attempting to bind, as a non-priv'd - * user, to 'privileged' ports (< 1024) on *nix - */ - AccessDenied -} -/// Details returned as part of a `Result::Err` result from `tcp::connect` -pub enum TcpConnectErrData { - /** - * Some unplanned-for error. The first and second fields correspond - * to libuv's `err_name` and `err_msg` fields, respectively. - */ - GenericConnectErr(~str, ~str), - /// Invalid IP or invalid port - ConnectionRefused -} - -/** - * Initiate a client connection over TCP/IP - * - * # Arguments - * - * * `input_ip` - The IP address (versions 4 or 6) of the remote host - * * `port` - the unsigned integer of the desired remote host port - * * `iotask` - a `uv::iotask` that the tcp request will run on - * - * # Returns - * - * A `result` that, if the operation succeeds, contains a - * `net::net::TcpSocket` that can be used to send and receive data to/from - * the remote host. In the event of failure, a - * `net::tcp::TcpConnectErrData` instance will be returned - */ -pub fn connect(input_ip: ip::IpAddr, port: uint, - iotask: &IoTask) - -> result::Result<TcpSocket, TcpConnectErrData> { - unsafe { - let (result_po, result_ch) = stream::<ConnAttempt>(); - let result_ch = SharedChan::new(result_ch); - let (closed_signal_po, closed_signal_ch) = stream::<()>(); - let closed_signal_ch = SharedChan::new(closed_signal_ch); - let conn_data = ConnectReqData { - result_ch: result_ch, - closed_signal_ch: closed_signal_ch - }; - let conn_data_ptr: *ConnectReqData = &conn_data; - let (reader_po, reader_ch) = stream::<Result<~[u8], TcpErrData>>(); - let reader_ch = SharedChan::new(reader_ch); - let stream_handle_ptr = malloc_uv_tcp_t(); - *(stream_handle_ptr as *mut uv::ll::uv_tcp_t) = uv::ll::tcp_t(); - let socket_data = @TcpSocketData { - reader_po: @reader_po, - reader_ch: reader_ch, - stream_handle_ptr: stream_handle_ptr, - connect_req: uv::ll::connect_t(), - write_req: uv::ll::write_t(), - ipv6: match input_ip { - ip::Ipv4(_) => { false } - ip::Ipv6(_) => { true } - }, - iotask: iotask.clone() - }; - let socket_data_ptr: *TcpSocketData = &*socket_data; - // get an unsafe representation of our stream_handle_ptr that - // we can send into the interact cb to be handled in libuv.. - debug!("stream_handle_ptr outside interact %?", - stream_handle_ptr); - do iotask::interact(iotask) |loop_ptr| { - unsafe { - debug!("in interact cb for tcp client connect.."); - debug!("stream_handle_ptr in interact %?", - stream_handle_ptr); - match uv::ll::tcp_init( loop_ptr, stream_handle_ptr) { - 0i32 => { - debug!("tcp_init successful"); - debug!("dealing w/ ipv4 connection.."); - let connect_req_ptr: *uv::ll::uv_connect_t = - &(*socket_data_ptr).connect_req; - let addr_str = ip::format_addr(&input_ip); - let connect_result = match input_ip { - ip::Ipv4(ref addr) => { - // have to "recreate" the - // sockaddr_in/6 since the ip_addr - // discards the port info.. should - // probably add an additional rust - // type that actually is closer to - // what the libuv API expects (ip str - // + port num) - debug!("addr: %?", addr); - let in_addr = uv::ll::ip4_addr(addr_str, - port as int); - uv::ll::tcp_connect( - connect_req_ptr, - stream_handle_ptr, - &in_addr, - tcp_connect_on_connect_cb) - } - ip::Ipv6(ref addr) => { - debug!("addr: %?", addr); - let in_addr = uv::ll::ip6_addr(addr_str, - port as int); - uv::ll::tcp_connect6( - connect_req_ptr, - stream_handle_ptr, - &in_addr, - tcp_connect_on_connect_cb) - } - }; - match connect_result { - 0i32 => { - debug!("tcp_connect successful: \ - stream %x, - socket data %x", - stream_handle_ptr as uint, - socket_data_ptr as uint); - // reusable data that we'll have for the - // duration.. - uv::ll::set_data_for_uv_handle( - stream_handle_ptr, - socket_data_ptr as - *libc::c_void); - // just so the connect_cb can send the - // outcome.. - uv::ll::set_data_for_req(connect_req_ptr, - conn_data_ptr); - debug!("leaving tcp_connect interact cb..."); - // let tcp_connect_on_connect_cb send on - // the result_ch, now.. - } - _ => { - // immediate connect - // failure.. probably a garbage ip or - // somesuch - let err_data = - uv::ll::get_last_err_data(loop_ptr); - let result_ch = (*conn_data_ptr) - .result_ch.clone(); - result_ch.send(ConnFailure(err_data)); - uv::ll::set_data_for_uv_handle( - stream_handle_ptr, - conn_data_ptr); - uv::ll::close(stream_handle_ptr, - stream_error_close_cb); - } - } - } - _ => { - // failure to create a tcp handle - let err_data = uv::ll::get_last_err_data(loop_ptr); - let result_ch = (*conn_data_ptr).result_ch.clone(); - result_ch.send(ConnFailure(err_data)); - } - } - } - } - match result_po.recv() { - ConnSuccess => { - debug!("tcp::connect - received success on result_po"); - result::Ok(TcpSocket(socket_data)) - } - ConnFailure(ref err_data) => { - closed_signal_po.recv(); - debug!("tcp::connect - received failure on result_po"); - // still have to free the malloc'd stream handle.. - rustrt::rust_uv_current_kernel_free(stream_handle_ptr - as *libc::c_void); - let tcp_conn_err = match err_data.err_name { - ~"ECONNREFUSED" => ConnectionRefused, - _ => GenericConnectErr(copy err_data.err_name, - copy err_data.err_msg) - }; - result::Err(tcp_conn_err) - } - } - } -} - -/** - * Write binary data to a tcp stream; Blocks until operation completes - * - * # Arguments - * - * * sock - a `TcpSocket` to write to - * * raw_write_data - a vector of `~[u8]` that will be written to the stream. - * This value must remain valid for the duration of the `write` call - * - * # Returns - * - * A `Result` object with a `()` value as the `Ok` variant, or a - * `TcpErrData` value as the `Err` variant - */ -pub fn write(sock: &TcpSocket, raw_write_data: ~[u8]) - -> result::Result<(), TcpErrData> { - let socket_data_ptr: *TcpSocketData = &*sock.socket_data; - write_common_impl(socket_data_ptr, raw_write_data) -} - -/** - * Write binary data to tcp stream; Returns a `future::Future` value - * immediately - * - * # Safety - * - * This function can produce unsafe results if: - * - * 1. the call to `write_future` is made - * 2. the `future::Future` value returned is never resolved via - * `Future::get` - * 3. and then the `TcpSocket` passed in to `write_future` leaves - * scope and is destructed before the task that runs the libuv write - * operation completes. - * - * As such: If using `write_future`, always be sure to resolve the returned - * `Future` so as to ensure libuv doesn't try to access a released write - * handle. Otherwise, use the blocking `tcp::write` function instead. - * - * # Arguments - * - * * sock - a `TcpSocket` to write to - * * raw_write_data - a vector of `~[u8]` that will be written to the stream. - * This value must remain valid for the duration of the `write` call - * - * # Returns - * - * A `Future` value that, once the `write` operation completes, resolves to a - * `Result` object with a `nil` value as the `Ok` variant, or a `TcpErrData` - * value as the `Err` variant - */ -pub fn write_future(sock: &TcpSocket, raw_write_data: ~[u8]) - -> future::Future<result::Result<(), TcpErrData>> -{ - let socket_data_ptr: *TcpSocketData = &*sock.socket_data; - do future_spawn { - let data_copy = copy(raw_write_data); - write_common_impl(socket_data_ptr, data_copy) - } -} - -/** - * Begin reading binary data from an open TCP connection; used with - * `read_stop` - * - * # Arguments - * - * * sock -- a `net::tcp::TcpSocket` for the connection to read from - * - * # Returns - * - * * A `Result` instance that will either contain a - * `core::comm::Port<Result<~[u8], TcpErrData>>` that the user can read - * (and * optionally, loop on) from until `read_stop` is called, or a - * `TcpErrData` record - */ -pub fn read_start(sock: &TcpSocket) - -> result::Result<@Port<result::Result<~[u8], - TcpErrData>>, - TcpErrData> { - let socket_data: *TcpSocketData = &*sock.socket_data; - read_start_common_impl(socket_data) -} - -/** - * Stop reading from an open TCP connection; used with `read_start` - * - * # Arguments - * - * * `sock` - a `net::tcp::TcpSocket` that you wish to stop reading on - */ -pub fn read_stop(sock: &TcpSocket) -> result::Result<(), TcpErrData> { - let socket_data: *TcpSocketData = &*sock.socket_data; - read_stop_common_impl(socket_data) -} - -/** - * Reads a single chunk of data from `TcpSocket`; block until data/error - * recv'd - * - * Does a blocking read operation for a single chunk of data from a - * `TcpSocket` until a data arrives or an error is received. The provided - * `timeout_msecs` value is used to raise an error if the timeout period - * passes without any data received. - * - * # Arguments - * - * * `sock` - a `net::tcp::TcpSocket` that you wish to read from - * * `timeout_msecs` - a `uint` value, in msecs, to wait before dropping the - * read attempt. Pass `0u` to wait indefinitely - */ -pub fn read(sock: &TcpSocket, timeout_msecs: uint) - -> result::Result<~[u8],TcpErrData> { - let socket_data: *TcpSocketData = &*sock.socket_data; - read_common_impl(socket_data, timeout_msecs) -} - -/** - * Reads a single chunk of data; returns a `future::Future<~[u8]>` - * immediately - * - * Does a non-blocking read operation for a single chunk of data from a - * `TcpSocket` and immediately returns a `Future` value representing the - * result. When resolving the returned `Future`, it will block until data - * arrives or an error is received. The provided `timeout_msecs` - * value is used to raise an error if the timeout period passes without any - * data received. - * - * # Safety - * - * This function can produce unsafe results if the call to `read_future` is - * made, the `future::Future` value returned is never resolved via - * `Future::get`, and then the `TcpSocket` passed in to `read_future` leaves - * scope and is destructed before the task that runs the libuv read - * operation completes. - * - * As such: If using `read_future`, always be sure to resolve the returned - * `Future` so as to ensure libuv doesn't try to access a released read - * handle. Otherwise, use the blocking `tcp::read` function instead. - * - * # Arguments - * - * * `sock` - a `net::tcp::TcpSocket` that you wish to read from - * * `timeout_msecs` - a `uint` value, in msecs, to wait before dropping the - * read attempt. Pass `0u` to wait indefinitely - */ -fn read_future(sock: &TcpSocket, timeout_msecs: uint) - -> future::Future<result::Result<~[u8],TcpErrData>> { - let socket_data: *TcpSocketData = &*sock.socket_data; - do future_spawn { - read_common_impl(socket_data, timeout_msecs) - } -} - -/** - * Bind an incoming client connection to a `net::tcp::TcpSocket` - * - * # Notes - * - * It is safe to call `net::tcp::accept` _only_ within the context of the - * `new_connect_cb` callback provided as the final argument to the - * `net::tcp::listen` function. - * - * The `new_conn` opaque value is provided _only_ as the first argument to the - * `new_connect_cb` provided as a part of `net::tcp::listen`. - * It can be safely sent to another task but it _must_ be - * used (via `net::tcp::accept`) before the `new_connect_cb` call it was - * provided to returns. - * - * This implies that a port/chan pair must be used to make sure that the - * `new_connect_cb` call blocks until an attempt to create a - * `net::tcp::TcpSocket` is completed. - * - * # Example - * - * Here, the `new_conn` is used in conjunction with `accept` from within - * a task spawned by the `new_connect_cb` passed into `listen` - * - * ~~~~~~~~~~~ - * do net::tcp::listen(remote_ip, remote_port, backlog, iotask, - * // this callback is ran once after the connection is successfully - * // set up - * |kill_ch| { - * // pass the kill_ch to your main loop or wherever you want - * // to be able to externally kill the server from - * }) - * // this callback is ran when a new connection arrives - * |new_conn, kill_ch| { - * let (cont_po, cont_ch) = comm::stream::<option::Option<TcpErrData>>(); - * do task::spawn { - * let accept_result = net::tcp::accept(new_conn); - * match accept_result { - * Err(accept_error) => { - * cont_ch.send(Some(accept_error)); - * // fail? - * }, - * Ok(sock) => { - * cont_ch.send(None); - * // do work here - * } - * } - * }; - * match cont_po.recv() { - * // shut down listen() - * Some(err_data) => kill_ch.send(Some(err_data)), - * // wait for next connection - * None => () - * } - * }; - * ~~~~~~~~~~~ - * - * # Arguments - * - * * `new_conn` - an opaque value used to create a new `TcpSocket` - * - * # Returns - * - * On success, this function will return a `net::tcp::TcpSocket` as the - * `Ok` variant of a `Result`. The `net::tcp::TcpSocket` is anchored within - * the task that `accept` was called within for its lifetime. On failure, - * this function will return a `net::tcp::TcpErrData` record - * as the `Err` variant of a `Result`. - */ -pub fn accept(new_conn: TcpNewConnection) - -> result::Result<TcpSocket, TcpErrData> { - unsafe { - match new_conn{ - NewTcpConn(server_handle_ptr) => { - let server_data_ptr = uv::ll::get_data_for_uv_handle( - server_handle_ptr) as *TcpListenFcData; - let (reader_po, reader_ch) = stream::< - Result<~[u8], TcpErrData>>(); - let reader_ch = SharedChan::new(reader_ch); - let iotask = &(*server_data_ptr).iotask; - let stream_handle_ptr = malloc_uv_tcp_t(); - *(stream_handle_ptr as *mut uv::ll::uv_tcp_t) = - uv::ll::tcp_t(); - let client_socket_data: @TcpSocketData = @TcpSocketData { - reader_po: @reader_po, - reader_ch: reader_ch, - stream_handle_ptr : stream_handle_ptr, - connect_req : uv::ll::connect_t(), - write_req : uv::ll::write_t(), - ipv6: (*server_data_ptr).ipv6, - iotask : iotask.clone() - }; - let client_socket_data_ptr: *TcpSocketData = - &*client_socket_data; - let client_stream_handle_ptr = - (*client_socket_data_ptr).stream_handle_ptr; - - let (result_po, result_ch) = stream::<Option<TcpErrData>>(); - let result_ch = SharedChan::new(result_ch); - - // UNSAFE LIBUV INTERACTION BEGIN - // .. normally this happens within the context of - // a call to uv::hl::interact.. but we're breaking - // the rules here because this always has to be - // called within the context of a listen() new_connect_cb - // callback (or it will likely fail and drown your cat) - debug!("in interact cb for tcp::accept"); - let loop_ptr = uv::ll::get_loop_for_uv_handle( - server_handle_ptr); - match uv::ll::tcp_init(loop_ptr, client_stream_handle_ptr) { - 0i32 => { - debug!("uv_tcp_init successful for \ - client stream"); - match uv::ll::accept( - server_handle_ptr as *libc::c_void, - client_stream_handle_ptr as *libc::c_void) { - 0i32 => { - debug!("successfully accepted client \ - connection: \ - stream %x, \ - socket data %x", - client_stream_handle_ptr as uint, - client_socket_data_ptr as uint); - uv::ll::set_data_for_uv_handle( - client_stream_handle_ptr, - client_socket_data_ptr - as *libc::c_void); - let ptr = uv::ll::get_data_for_uv_handle( - client_stream_handle_ptr); - debug!("ptrs: %x %x", - client_socket_data_ptr as uint, - ptr as uint); - result_ch.send(None); - } - _ => { - debug!("failed to accept client conn"); - result_ch.send(Some( - uv::ll::get_last_err_data( - loop_ptr).to_tcp_err())); - } - } - } - _ => { - debug!("failed to accept client stream"); - result_ch.send(Some( - uv::ll::get_last_err_data( - loop_ptr).to_tcp_err())); - } - } - // UNSAFE LIBUV INTERACTION END - match result_po.recv() { - Some(copy err_data) => result::Err(err_data), - None => result::Ok(TcpSocket(client_socket_data)) - } - } - } - } -} - -/** - * Bind to a given IP/port and listen for new connections - * - * # Arguments - * - * * `host_ip` - a `net::ip::IpAddr` representing a unique IP - * (versions 4 or 6) - * * `port` - a uint representing the port to listen on - * * `backlog` - a uint representing the number of incoming connections - * to cache in memory - * * `hl_loop` - a `uv_iotask::IoTask` that the tcp request will run on - * * `on_establish_cb` - a callback that is evaluated if/when the listener - * is successfully established. it takes no parameters - * * `new_connect_cb` - a callback to be evaluated, on the libuv thread, - * whenever a client attempts to conect on the provided ip/port. the - * callback's arguments are: - * * `new_conn` - an opaque type that can be passed to - * `net::tcp::accept` in order to be converted to a `TcpSocket`. - * * `kill_ch` - channel of type `core::comm::Chan<Option<tcp_err_data>>`. - * this channel can be used to send a message to cause `listen` to begin - * closing the underlying libuv data structures. - * - * # returns - * - * a `Result` instance containing empty data of type `()` on a - * successful/normal shutdown, and a `TcpListenErrData` enum in the event - * of listen exiting because of an error - */ -pub fn listen(host_ip: ip::IpAddr, port: uint, backlog: uint, - iotask: &IoTask, - on_establish_cb: ~fn(SharedChan<Option<TcpErrData>>), - new_connect_cb: ~fn(TcpNewConnection, - SharedChan<Option<TcpErrData>>)) - -> result::Result<(), TcpListenErrData> { - do listen_common(host_ip, port, backlog, iotask, - on_establish_cb) - // on_connect_cb - |handle| { - unsafe { - let server_data_ptr = uv::ll::get_data_for_uv_handle(handle) - as *TcpListenFcData; - let new_conn = NewTcpConn(handle); - let kill_ch = (*server_data_ptr).kill_ch.clone(); - new_connect_cb(new_conn, kill_ch); - } - } -} - -fn listen_common(host_ip: ip::IpAddr, - port: uint, - backlog: uint, - iotask: &IoTask, - on_establish_cb: ~fn(SharedChan<Option<TcpErrData>>), - on_connect_cb: ~fn(*uv::ll::uv_tcp_t)) - -> result::Result<(), TcpListenErrData> { - let (stream_closed_po, stream_closed_ch) = stream::<()>(); - let stream_closed_ch = SharedChan::new(stream_closed_ch); - let (kill_po, kill_ch) = stream::<Option<TcpErrData>>(); - let kill_ch = SharedChan::new(kill_ch); - let server_stream = uv::ll::tcp_t(); - let server_stream_ptr: *uv::ll::uv_tcp_t = &server_stream; - let server_data: TcpListenFcData = TcpListenFcData { - server_stream_ptr: server_stream_ptr, - stream_closed_ch: stream_closed_ch, - kill_ch: kill_ch.clone(), - on_connect_cb: on_connect_cb, - iotask: iotask.clone(), - ipv6: match &host_ip { - &ip::Ipv4(_) => { false } - &ip::Ipv6(_) => { true } - }, - active: @mut true - }; - let server_data_ptr: *TcpListenFcData = &server_data; - - let (setup_po, setup_ch) = stream(); - - // this is to address a compiler warning about - // an implicit copy.. it seems that double nested - // will defeat a move sigil, as is done to the host_ip - // arg above.. this same pattern works w/o complaint in - // tcp::connect (because the iotask::interact cb isn't - // nested within a core::comm::listen block) - let loc_ip = copy(host_ip); - do iotask::interact(iotask) |loop_ptr| { - unsafe { - match uv::ll::tcp_init(loop_ptr, server_stream_ptr) { - 0i32 => { - uv::ll::set_data_for_uv_handle( - server_stream_ptr, - server_data_ptr); - let addr_str = ip::format_addr(&loc_ip); - let bind_result = match loc_ip { - ip::Ipv4(ref addr) => { - debug!("addr: %?", addr); - let in_addr = uv::ll::ip4_addr( - addr_str, - port as int); - uv::ll::tcp_bind(server_stream_ptr, &in_addr) - } - ip::Ipv6(ref addr) => { - debug!("addr: %?", addr); - let in_addr = uv::ll::ip6_addr( - addr_str, - port as int); - uv::ll::tcp_bind6(server_stream_ptr, &in_addr) - } - }; - match bind_result { - 0i32 => { - match uv::ll::listen( - server_stream_ptr, - backlog as libc::c_int, - tcp_lfc_on_connection_cb) { - 0i32 => setup_ch.send(None), - _ => { - debug!( - "failure to uv_tcp_init"); - let err_data = - uv::ll::get_last_err_data( - loop_ptr); - setup_ch.send(Some(err_data)); - } - } - } - _ => { - debug!("failure to uv_tcp_bind"); - let err_data = uv::ll::get_last_err_data( - loop_ptr); - setup_ch.send(Some(err_data)); - } - } - } - _ => { - debug!("failure to uv_tcp_bind"); - let err_data = uv::ll::get_last_err_data( - loop_ptr); - setup_ch.send(Some(err_data)); - } - } - } - } - - let setup_result = setup_po.recv(); - - match setup_result { - Some(ref err_data) => { - do iotask::interact(iotask) |loop_ptr| { - unsafe { - debug!( - "tcp::listen post-kill recv hl interact %?", - loop_ptr); - *(*server_data_ptr).active = false; - uv::ll::close(server_stream_ptr, tcp_lfc_close_cb); - } - }; - stream_closed_po.recv(); - match err_data.err_name { - ~"EACCES" => { - debug!("Got EACCES error"); - result::Err(AccessDenied) - } - ~"EADDRINUSE" => { - debug!("Got EADDRINUSE error"); - result::Err(AddressInUse) - } - _ => { - debug!("Got '%s' '%s' libuv error", - err_data.err_name, err_data.err_msg); - result::Err( - GenericListenErr(copy err_data.err_name, - copy err_data.err_msg)) - } - } - } - None => { - on_establish_cb(kill_ch.clone()); - let kill_result = kill_po.recv(); - do iotask::interact(iotask) |loop_ptr| { - unsafe { - debug!( - "tcp::listen post-kill recv hl interact %?", - loop_ptr); - *(*server_data_ptr).active = false; - uv::ll::close(server_stream_ptr, tcp_lfc_close_cb); - } - }; - stream_closed_po.recv(); - match kill_result { - // some failure post bind/listen - Some(ref err_data) => result::Err(GenericListenErr( - copy err_data.err_name, - copy err_data.err_msg)), - // clean exit - None => result::Ok(()) - } - } - } -} - - -/** - * Convert a `net::tcp::TcpSocket` to a `net::tcp::TcpSocketBuf`. - * - * This function takes ownership of a `net::tcp::TcpSocket`, returning it - * stored within a buffered wrapper, which can be converted to a `io::Reader` - * or `io::Writer` - * - * # Arguments - * - * * `sock` -- a `net::tcp::TcpSocket` that you want to buffer - * - * # Returns - * - * A buffered wrapper that you can cast as an `io::Reader` or `io::Writer` - */ -pub fn socket_buf(sock: TcpSocket) -> TcpSocketBuf { - TcpSocketBuf(@mut TcpBufferedSocketData { - sock: sock, buf: ~[], buf_off: 0 - }) -} - -/// Convenience methods extending `net::tcp::TcpSocket` -pub impl TcpSocket { - pub fn read_start(&self) -> result::Result<@Port< - result::Result<~[u8], TcpErrData>>, TcpErrData> { - read_start(self) - } - pub fn read_stop(&self) -> - result::Result<(), TcpErrData> { - read_stop(self) - } - fn read(&self, timeout_msecs: uint) -> - result::Result<~[u8], TcpErrData> { - read(self, timeout_msecs) - } - fn read_future(&self, timeout_msecs: uint) -> - future::Future<result::Result<~[u8], TcpErrData>> { - read_future(self, timeout_msecs) - } - pub fn write(&self, raw_write_data: ~[u8]) - -> result::Result<(), TcpErrData> { - write(self, raw_write_data) - } - pub fn write_future(&self, raw_write_data: ~[u8]) - -> future::Future<result::Result<(), TcpErrData>> { - write_future(self, raw_write_data) - } - pub fn get_peer_addr(&self) -> ip::IpAddr { - unsafe { - if self.socket_data.ipv6 { - let addr = uv::ll::ip6_addr("", 0); - uv::ll::tcp_getpeername6(self.socket_data.stream_handle_ptr, - &addr); - ip::Ipv6(addr) - } else { - let addr = uv::ll::ip4_addr("", 0); - uv::ll::tcp_getpeername(self.socket_data.stream_handle_ptr, - &addr); - ip::Ipv4(addr) - } - } - } -} - -/// Implementation of `io::Reader` trait for a buffered `net::tcp::TcpSocket` -impl io::Reader for TcpSocketBuf { - fn read(&self, buf: &mut [u8], len: uint) -> uint { - if len == 0 { return 0 } - let mut count: uint = 0; - - loop { - assert!(count < len); - - // If possible, copy up to `len` bytes from the internal - // `data.buf` into `buf` - let nbuffered = vec::uniq_len(&const self.data.buf) - - self.data.buf_off; - let needed = len - count; - if nbuffered > 0 { - unsafe { - let ncopy = uint::min(nbuffered, needed); - let dst = ptr::mut_offset( - vec::raw::to_mut_ptr(buf), count); - let src = ptr::offset( - vec::raw::to_ptr(self.data.buf), - self.data.buf_off); - ptr::copy_memory(dst, src, ncopy); - self.data.buf_off += ncopy; - count += ncopy; - } - } - - assert!(count <= len); - if count == len { - break; - } - - // We copied all the bytes we had in the internal buffer into - // the result buffer, but the caller wants more bytes, so we - // need to read in data from the socket. Note that the internal - // buffer is of no use anymore as we read all bytes from it, - // so we can throw it away. - let read_result = { - let data = &*self.data; - read(&data.sock, 0) - }; - if read_result.is_err() { - let err_data = read_result.get_err(); - - if err_data.err_name == ~"EOF" { - *self.end_of_stream = true; - break; - } else { - debug!("ERROR sock_buf as io::reader.read err %? %?", - err_data.err_name, err_data.err_msg); - // As we have already copied data into result buffer, - // we cannot simply return 0 here. Instead the error - // should show up in a later call to read(). - break; - } - } else { - self.data.buf = result::unwrap(read_result); - self.data.buf_off = 0; - } - } - - count - } - fn read_byte(&self) -> int { - loop { - if vec::uniq_len(&const self.data.buf) > self.data.buf_off { - let c = self.data.buf[self.data.buf_off]; - self.data.buf_off += 1; - return c as int - } - - let read_result = { - let data = &*self.data; - read(&data.sock, 0) - }; - if read_result.is_err() { - let err_data = read_result.get_err(); - - if err_data.err_name == ~"EOF" { - *self.end_of_stream = true; - return -1 - } else { - debug!("ERROR sock_buf as io::reader.read err %? %?", - err_data.err_name, err_data.err_msg); - fail!() - } - } else { - self.data.buf = result::unwrap(read_result); - self.data.buf_off = 0; - } - } - } - fn eof(&self) -> bool { - *self.end_of_stream - } - fn seek(&self, dist: int, seek: io::SeekStyle) { - debug!("tcp_socket_buf seek stub %? %?", dist, seek); - // noop - } - fn tell(&self) -> uint { - 0u // noop - } -} - -/// Implementation of `io::Reader` trait for a buffered `net::tcp::TcpSocket` -impl io::Writer for TcpSocketBuf { - pub fn write(&self, data: &[u8]) { - let socket_data_ptr: *TcpSocketData = - &(*((*(self.data)).sock).socket_data); - let w_result = write_common_impl(socket_data_ptr, - vec::slice(data, - 0, - data.len()).to_vec()); - if w_result.is_err() { - let err_data = w_result.get_err(); - debug!( - "ERROR sock_buf as io::writer.writer err: %? %?", - err_data.err_name, err_data.err_msg); - } - } - fn seek(&self, dist: int, seek: io::SeekStyle) { - debug!("tcp_socket_buf seek stub %? %?", dist, seek); - // noop - } - fn tell(&self) -> uint { - 0u - } - fn flush(&self) -> int { - 0 - } - fn get_type(&self) -> io::WriterType { - io::File - } -} - -// INTERNAL API - -fn tear_down_socket_data(socket_data: @TcpSocketData) { - unsafe { - let (closed_po, closed_ch) = stream::<()>(); - let closed_ch = SharedChan::new(closed_ch); - let close_data = TcpSocketCloseData { - closed_ch: closed_ch - }; - let close_data_ptr: *TcpSocketCloseData = &close_data; - let stream_handle_ptr = (*socket_data).stream_handle_ptr; - do iotask::interact(&(*socket_data).iotask) |loop_ptr| { - unsafe { - debug!( - "interact dtor for tcp_socket stream %? loop %?", - stream_handle_ptr, loop_ptr); - uv::ll::set_data_for_uv_handle(stream_handle_ptr, - close_data_ptr); - uv::ll::close(stream_handle_ptr, tcp_socket_dtor_close_cb); - } - }; - closed_po.recv(); - //the line below will most likely crash - //log(debug, fmt!("about to free socket_data at %?", socket_data)); - rustrt::rust_uv_current_kernel_free(stream_handle_ptr - as *libc::c_void); - debug!("exiting dtor for tcp_socket"); - } -} - -// shared implementation for tcp::read -fn read_common_impl(socket_data: *TcpSocketData, timeout_msecs: uint) - -> result::Result<~[u8],TcpErrData> { - unsafe { - use timer; - - debug!("starting tcp::read"); - let iotask = &(*socket_data).iotask; - let rs_result = read_start_common_impl(socket_data); - if result::is_err(&rs_result) { - let err_data = result::get_err(&rs_result); - result::Err(err_data) - } - else { - debug!("tcp::read before recv_timeout"); - let read_result = if timeout_msecs > 0u { - timer::recv_timeout( - iotask, timeout_msecs, result::unwrap(rs_result)) - } else { - Some(result::get(&rs_result).recv()) - }; - debug!("tcp::read after recv_timeout"); - match read_result { - None => { - debug!("tcp::read: timed out.."); - let err_data = TcpErrData { - err_name: ~"TIMEOUT", - err_msg: ~"req timed out" - }; - read_stop_common_impl(socket_data); - result::Err(err_data) - } - Some(data_result) => { - debug!("tcp::read got data"); - read_stop_common_impl(socket_data); - data_result - } - } - } - } -} - -// shared impl for read_stop -fn read_stop_common_impl(socket_data: *TcpSocketData) -> - result::Result<(), TcpErrData> { - unsafe { - let stream_handle_ptr = (*socket_data).stream_handle_ptr; - let (stop_po, stop_ch) = stream::<Option<TcpErrData>>(); - do iotask::interact(&(*socket_data).iotask) |loop_ptr| { - unsafe { - debug!("in interact cb for tcp::read_stop"); - match uv::ll::read_stop(stream_handle_ptr - as *uv::ll::uv_stream_t) { - 0i32 => { - debug!("successfully called uv_read_stop"); - stop_ch.send(None); - } - _ => { - debug!("failure in calling uv_read_stop"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - stop_ch.send(Some(err_data.to_tcp_err())); - } - } - } - } - match stop_po.recv() { - Some(err_data) => Err(err_data), - None => Ok(()) - } - } -} - -// shared impl for read_start -fn read_start_common_impl(socket_data: *TcpSocketData) - -> result::Result<@Port< - result::Result<~[u8], TcpErrData>>, TcpErrData> { - unsafe { - let stream_handle_ptr = (*socket_data).stream_handle_ptr; - let (start_po, start_ch) = stream::<Option<uv::ll::uv_err_data>>(); - debug!("in tcp::read_start before interact loop"); - do iotask::interact(&(*socket_data).iotask) |loop_ptr| { - unsafe { - debug!("in tcp::read_start interact cb %?", - loop_ptr); - match uv::ll::read_start(stream_handle_ptr - as *uv::ll::uv_stream_t, - on_alloc_cb, - on_tcp_read_cb) { - 0i32 => { - debug!("success doing uv_read_start"); - start_ch.send(None); - } - _ => { - debug!("error attempting uv_read_start"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - start_ch.send(Some(err_data)); - } - } - } - } - match start_po.recv() { - Some(ref err_data) => result::Err( - err_data.to_tcp_err()), - None => { - result::Ok((*socket_data).reader_po) - } - } - } -} - -// helper to convert a "class" vector of [u8] to a *[uv::ll::uv_buf_t] - -// shared implementation used by write and write_future -fn write_common_impl(socket_data_ptr: *TcpSocketData, - raw_write_data: ~[u8]) - -> result::Result<(), TcpErrData> { - unsafe { - let write_req_ptr: *uv::ll::uv_write_t = - &(*socket_data_ptr).write_req; - let stream_handle_ptr = - (*socket_data_ptr).stream_handle_ptr; - let write_buf_vec = ~[ - uv::ll::buf_init(vec::raw::to_ptr(raw_write_data), - raw_write_data.len()) - ]; - let write_buf_vec_ptr: *~[uv::ll::uv_buf_t] = &write_buf_vec; - let (result_po, result_ch) = stream::<TcpWriteResult>(); - let result_ch = SharedChan::new(result_ch); - let write_data = WriteReqData { - result_ch: result_ch - }; - let write_data_ptr: *WriteReqData = &write_data; - do iotask::interact(&(*socket_data_ptr).iotask) |loop_ptr| { - unsafe { - debug!("in interact cb for tcp::write %?", - loop_ptr); - match uv::ll::write(write_req_ptr, - stream_handle_ptr, - write_buf_vec_ptr, - tcp_write_complete_cb) { - 0i32 => { - debug!("uv_write() invoked successfully"); - uv::ll::set_data_for_req(write_req_ptr, - write_data_ptr); - } - _ => { - debug!("error invoking uv_write()"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - let result_ch = (*write_data_ptr).result_ch.clone(); - result_ch.send(TcpWriteError(err_data.to_tcp_err())); - } - } - } - } - // FIXME (#2656): Instead of passing unsafe pointers to local data, - // and waiting here for the write to complete, we should transfer - // ownership of everything to the I/O task and let it deal with the - // aftermath, so we don't have to sit here blocking. - match result_po.recv() { - TcpWriteSuccess => Ok(()), - TcpWriteError(err_data) => Err(err_data) - } - } -} - -enum TcpNewConnection { - NewTcpConn(*uv::ll::uv_tcp_t) -} - -struct TcpListenFcData { - server_stream_ptr: *uv::ll::uv_tcp_t, - stream_closed_ch: SharedChan<()>, - kill_ch: SharedChan<Option<TcpErrData>>, - on_connect_cb: ~fn(*uv::ll::uv_tcp_t), - iotask: IoTask, - ipv6: bool, - active: @mut bool, -} - -extern fn tcp_lfc_close_cb(handle: *uv::ll::uv_tcp_t) { - unsafe { - let server_data_ptr = uv::ll::get_data_for_uv_handle( - handle) as *TcpListenFcData; - let stream_closed_ch = (*server_data_ptr).stream_closed_ch.clone(); - stream_closed_ch.send(()); - } -} - -extern fn tcp_lfc_on_connection_cb(handle: *uv::ll::uv_tcp_t, - status: libc::c_int) { - unsafe { - let server_data_ptr = uv::ll::get_data_for_uv_handle(handle) - as *TcpListenFcData; - let kill_ch = (*server_data_ptr).kill_ch.clone(); - if *(*server_data_ptr).active { - match status { - 0i32 => ((*server_data_ptr).on_connect_cb)(handle), - _ => { - let loop_ptr = uv::ll::get_loop_for_uv_handle(handle); - kill_ch.send( - Some(uv::ll::get_last_err_data(loop_ptr) - .to_tcp_err())); - *(*server_data_ptr).active = false; - } - } - } - } -} - -fn malloc_uv_tcp_t() -> *uv::ll::uv_tcp_t { - unsafe { - rustrt::rust_uv_current_kernel_malloc( - rustrt::rust_uv_helper_uv_tcp_t_size()) as *uv::ll::uv_tcp_t - } -} - -enum TcpConnectResult { - TcpConnected(TcpSocket), - TcpConnectError(TcpErrData) -} - -enum TcpWriteResult { - TcpWriteSuccess, - TcpWriteError(TcpErrData) -} - -enum TcpReadStartResult { - TcpReadStartSuccess(Port<TcpReadResult>), - TcpReadStartError(TcpErrData) -} - -enum TcpReadResult { - TcpReadData(~[u8]), - TcpReadDone, - TcpReadErr(TcpErrData) -} - -trait ToTcpErr { - fn to_tcp_err(&self) -> TcpErrData; -} - -impl ToTcpErr for uv::ll::uv_err_data { - fn to_tcp_err(&self) -> TcpErrData { - TcpErrData { err_name: copy self.err_name, err_msg: copy self.err_msg } - } -} - -extern fn on_tcp_read_cb(stream: *uv::ll::uv_stream_t, - nread: libc::ssize_t, - buf: uv::ll::uv_buf_t) { - unsafe { - debug!("entering on_tcp_read_cb stream: %x nread: %?", - stream as uint, nread); - let loop_ptr = uv::ll::get_loop_for_uv_handle(stream); - let socket_data_ptr = uv::ll::get_data_for_uv_handle(stream) - as *TcpSocketData; - debug!("socket data is %x", socket_data_ptr as uint); - match nread as int { - // incoming err.. probably eof - -1 => { - let err_data = uv::ll::get_last_err_data(loop_ptr).to_tcp_err(); - debug!("on_tcp_read_cb: incoming err.. name %? msg %?", - err_data.err_name, err_data.err_msg); - let reader_ch = &(*socket_data_ptr).reader_ch; - reader_ch.send(result::Err(err_data)); - } - // do nothing .. unneeded buf - 0 => (), - // have data - _ => { - // we have data - debug!("tcp on_read_cb nread: %d", nread as int); - let reader_ch = &(*socket_data_ptr).reader_ch; - let buf_base = uv::ll::get_base_from_buf(buf); - let new_bytes = vec::from_buf(buf_base, nread as uint); - reader_ch.send(result::Ok(new_bytes)); - } - } - uv::ll::free_base_of_buf(buf); - debug!("exiting on_tcp_read_cb"); - } -} - -extern fn on_alloc_cb(handle: *libc::c_void, - suggested_size: size_t) - -> uv::ll::uv_buf_t { - unsafe { - debug!("tcp read on_alloc_cb!"); - let char_ptr = uv::ll::malloc_buf_base_of(suggested_size); - debug!("tcp read on_alloc_cb h: %? char_ptr: %u sugsize: %u", - handle, - char_ptr as uint, - suggested_size as uint); - uv::ll::buf_init(char_ptr, suggested_size as uint) - } -} - -struct TcpSocketCloseData { - closed_ch: SharedChan<()>, -} - -extern fn tcp_socket_dtor_close_cb(handle: *uv::ll::uv_tcp_t) { - unsafe { - let data = uv::ll::get_data_for_uv_handle(handle) - as *TcpSocketCloseData; - let closed_ch = (*data).closed_ch.clone(); - closed_ch.send(()); - debug!("tcp_socket_dtor_close_cb exiting.."); - } -} - -extern fn tcp_write_complete_cb(write_req: *uv::ll::uv_write_t, - status: libc::c_int) { - unsafe { - let write_data_ptr = uv::ll::get_data_for_req(write_req) - as *WriteReqData; - if status == 0i32 { - debug!("successful write complete"); - let result_ch = (*write_data_ptr).result_ch.clone(); - result_ch.send(TcpWriteSuccess); - } else { - let stream_handle_ptr = uv::ll::get_stream_handle_from_write_req( - write_req); - let loop_ptr = uv::ll::get_loop_for_uv_handle(stream_handle_ptr); - let err_data = uv::ll::get_last_err_data(loop_ptr); - debug!("failure to write"); - let result_ch = (*write_data_ptr).result_ch.clone(); - result_ch.send(TcpWriteError(err_data.to_tcp_err())); - } - } -} - -struct WriteReqData { - result_ch: SharedChan<TcpWriteResult>, -} - -struct ConnectReqData { - result_ch: SharedChan<ConnAttempt>, - closed_signal_ch: SharedChan<()>, -} - -extern fn stream_error_close_cb(handle: *uv::ll::uv_tcp_t) { - unsafe { - let data = uv::ll::get_data_for_uv_handle(handle) as - *ConnectReqData; - let closed_signal_ch = (*data).closed_signal_ch.clone(); - closed_signal_ch.send(()); - debug!("exiting steam_error_close_cb for %?", handle); - } -} - -extern fn tcp_connect_close_cb(handle: *uv::ll::uv_tcp_t) { - debug!("closed client tcp handle %?", handle); -} - -extern fn tcp_connect_on_connect_cb(connect_req_ptr: *uv::ll::uv_connect_t, - status: libc::c_int) { - unsafe { - let conn_data_ptr = (uv::ll::get_data_for_req(connect_req_ptr) - as *ConnectReqData); - let result_ch = (*conn_data_ptr).result_ch.clone(); - debug!("tcp_connect result_ch %?", result_ch); - let tcp_stream_ptr = - uv::ll::get_stream_handle_from_connect_req(connect_req_ptr); - match status { - 0i32 => { - debug!("successful tcp connection!"); - result_ch.send(ConnSuccess); - } - _ => { - debug!("error in tcp_connect_on_connect_cb"); - let loop_ptr = uv::ll::get_loop_for_uv_handle(tcp_stream_ptr); - let err_data = uv::ll::get_last_err_data(loop_ptr); - debug!("err_data %? %?", err_data.err_name, - err_data.err_msg); - result_ch.send(ConnFailure(err_data)); - uv::ll::set_data_for_uv_handle(tcp_stream_ptr, - conn_data_ptr); - uv::ll::close(tcp_stream_ptr, stream_error_close_cb); - } - } - debug!("leaving tcp_connect_on_connect_cb"); - } -} - -enum ConnAttempt { - ConnSuccess, - ConnFailure(uv::ll::uv_err_data) -} - -struct TcpSocketData { - reader_po: @Port<result::Result<~[u8], TcpErrData>>, - reader_ch: SharedChan<result::Result<~[u8], TcpErrData>>, - stream_handle_ptr: *uv::ll::uv_tcp_t, - connect_req: uv::ll::uv_connect_t, - write_req: uv::ll::uv_write_t, - ipv6: bool, - iotask: IoTask, -} - -struct TcpBufferedSocketData { - sock: TcpSocket, - buf: ~[u8], - buf_off: uint -} - -#[cfg(test)] -mod test { - use net::ip; - use net::tcp::{GenericListenErr, TcpConnectErrData, TcpListenErrData}; - use net::tcp::{connect, accept, read, listen, TcpSocket, socket_buf}; - use net; - use uv::iotask::IoTask; - use uv; - - use core::cell::Cell; - use core::comm::{stream, SharedChan}; - - // FIXME don't run on fbsd or linux 32 bit (#2064) - #[cfg(target_os="win32")] - #[cfg(target_os="darwin")] - #[cfg(target_os="linux")] - #[cfg(target_os="android")] - mod tcp_ipv4_server_and_client_test { - #[cfg(target_arch="x86_64")] - mod impl64 { - use net::tcp::test::*; - - #[test] - fn test_gl_tcp_server_and_client_ipv4() { - unsafe { - impl_gl_tcp_ipv4_server_and_client(); - } - } - #[test] - fn test_gl_tcp_get_peer_addr() { - unsafe { - impl_gl_tcp_ipv4_get_peer_addr(); - } - } - #[test] - fn test_gl_tcp_ipv4_client_error_connection_refused() { - unsafe { - impl_gl_tcp_ipv4_client_error_connection_refused(); - } - } - #[test] - fn test_gl_tcp_server_address_in_use() { - unsafe { - impl_gl_tcp_ipv4_server_address_in_use(); - } - } - #[test] - fn test_gl_tcp_server_access_denied() { - unsafe { - impl_gl_tcp_ipv4_server_access_denied(); - } - } - // Strange failure on Windows. --pcwalton - #[test] - #[ignore(cfg(target_os = "win32"))] - fn test_gl_tcp_ipv4_server_client_reader_writer() { - impl_gl_tcp_ipv4_server_client_reader_writer(); - } - #[test] - fn test_tcp_socket_impl_reader_handles_eof() { - impl_tcp_socket_impl_reader_handles_eof(); - } - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - mod impl32 { - use net::tcp::test::*; - - #[test] - #[ignore(cfg(target_os = "linux"))] - fn test_gl_tcp_server_and_client_ipv4() { - unsafe { - impl_gl_tcp_ipv4_server_and_client(); - } - } - #[test] - #[ignore(cfg(target_os = "linux"))] - fn test_gl_tcp_get_peer_addr() { - unsafe { - impl_gl_tcp_ipv4_get_peer_addr(); - } - } - #[test] - #[ignore(cfg(target_os = "linux"))] - fn test_gl_tcp_ipv4_client_error_connection_refused() { - unsafe { - impl_gl_tcp_ipv4_client_error_connection_refused(); - } - } - #[test] - #[ignore(cfg(target_os = "linux"))] - fn test_gl_tcp_server_address_in_use() { - unsafe { - impl_gl_tcp_ipv4_server_address_in_use(); - } - } - #[test] - #[ignore(cfg(target_os = "linux"))] - #[ignore(cfg(windows), reason = "deadlocking bots")] - fn test_gl_tcp_server_access_denied() { - unsafe { - impl_gl_tcp_ipv4_server_access_denied(); - } - } - #[test] - #[ignore(cfg(target_os = "linux"))] - #[ignore(cfg(target_os = "win32"))] - fn test_gl_tcp_ipv4_server_client_reader_writer() { - impl_gl_tcp_ipv4_server_client_reader_writer(); - } - } - } - pub fn impl_gl_tcp_ipv4_server_and_client() { - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 8888u; - let expected_req = ~"ping"; - let expected_resp = "pong"; - - let (server_result_po, server_result_ch) = stream::<~str>(); - - let (cont_po, cont_ch) = stream::<()>(); - let cont_ch = SharedChan::new(cont_ch); - // server - let hl_loop_clone = hl_loop.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - let cont_ch = cont_ch.clone(); - let actual_req = run_tcp_test_server( - server_ip, - server_port, - expected_resp.to_str(), - cont_ch.clone(), - &hl_loop_clone); - server_result_ch.send(actual_req); - }; - cont_po.recv(); - // client - debug!("server started, firing up client.."); - let actual_resp_result = run_tcp_test_client( - server_ip, - server_port, - expected_req, - hl_loop); - assert!(actual_resp_result.is_ok()); - let actual_resp = actual_resp_result.get(); - let actual_req = server_result_po.recv(); - debug!("REQ: expected: '%s' actual: '%s'", - expected_req, actual_req); - debug!("RESP: expected: '%s' actual: '%s'", - expected_resp, actual_resp); - assert!(str::contains(actual_req, expected_req)); - assert!(str::contains(actual_resp, expected_resp)); - } - pub fn impl_gl_tcp_ipv4_get_peer_addr() { - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 8887u; - let expected_resp = "pong"; - - let (cont_po, cont_ch) = stream::<()>(); - let cont_ch = SharedChan::new(cont_ch); - // server - let hl_loop_clone = hl_loop.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - let cont_ch = cont_ch.clone(); - run_tcp_test_server( - server_ip, - server_port, - expected_resp.to_str(), - cont_ch.clone(), - &hl_loop_clone); - }; - cont_po.recv(); - // client - debug!("server started, firing up client.."); - let server_ip_addr = ip::v4::parse_addr(server_ip); - let iotask = uv::global_loop::get(); - let connect_result = connect(server_ip_addr, server_port, - &iotask); - - let sock = result::unwrap(connect_result); - - debug!("testing peer address"); - // This is what we are actually testing! - assert!(net::ip::format_addr(&sock.get_peer_addr()) == - ~"127.0.0.1"); - assert_eq!(net::ip::get_port(&sock.get_peer_addr()), 8887); - - // Fulfill the protocol the test server expects - let resp_bytes = str::to_bytes(~"ping"); - tcp_write_single(&sock, resp_bytes); - debug!("message sent"); - sock.read(0u); - debug!("result read"); - } - pub fn impl_gl_tcp_ipv4_client_error_connection_refused() { - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 8889u; - let expected_req = ~"ping"; - // client - debug!("firing up client.."); - let actual_resp_result = run_tcp_test_client( - server_ip, - server_port, - expected_req, - hl_loop); - match actual_resp_result.get_err() { - ConnectionRefused => (), - _ => fail!("unknown error.. expected connection_refused") - } - } - pub fn impl_gl_tcp_ipv4_server_address_in_use() { - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 8890u; - let expected_req = ~"ping"; - let expected_resp = "pong"; - - let (cont_po, cont_ch) = stream::<()>(); - let cont_ch = SharedChan::new(cont_ch); - // server - let hl_loop_clone = hl_loop.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - let cont_ch = cont_ch.clone(); - run_tcp_test_server( - server_ip, - server_port, - expected_resp.to_str(), - cont_ch.clone(), - &hl_loop_clone); - } - cont_po.recv(); - // this one should fail.. - let listen_err = run_tcp_test_server_fail( - server_ip, - server_port, - hl_loop); - // client.. just doing this so that the first server tears down - debug!("server started, firing up client.."); - run_tcp_test_client( - server_ip, - server_port, - expected_req, - hl_loop); - match listen_err { - AddressInUse => { - assert!(true); - } - _ => { - fail!("expected address_in_use listen error, \ - but got a different error varient. check logs."); - } - } - } - pub fn impl_gl_tcp_ipv4_server_access_denied() { - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 80u; - // this one should fail.. - let listen_err = run_tcp_test_server_fail( - server_ip, - server_port, - hl_loop); - match listen_err { - AccessDenied => { - assert!(true); - } - _ => { - fail!("expected address_in_use listen error, \ - but got a different error varient. check logs."); - } - } - } - pub fn impl_gl_tcp_ipv4_server_client_reader_writer() { - - let iotask = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 8891u; - let expected_req = ~"ping"; - let expected_resp = "pong"; - - let (server_result_po, server_result_ch) = stream::<~str>(); - - let (cont_po, cont_ch) = stream::<()>(); - let cont_ch = SharedChan::new(cont_ch); - // server - let iotask_clone = iotask.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - let cont_ch = cont_ch.clone(); - let actual_req = run_tcp_test_server( - server_ip, - server_port, - expected_resp.to_str(), - cont_ch.clone(), - &iotask_clone); - server_result_ch.send(actual_req); - }; - cont_po.recv(); - // client - let server_addr = ip::v4::parse_addr(server_ip); - let conn_result = connect(server_addr, server_port, iotask); - if result::is_err(&conn_result) { - assert!(false); - } - let sock_buf = @socket_buf(result::unwrap(conn_result)); - buf_write(sock_buf, expected_req); - - // so contrived! - let actual_resp = do str::as_bytes(&expected_resp.to_str()) |resp_buf| { - buf_read(sock_buf, resp_buf.len()) - }; - - let actual_req = server_result_po.recv(); - debug!("REQ: expected: '%s' actual: '%s'", - expected_req, actual_req); - debug!("RESP: expected: '%s' actual: '%s'", - expected_resp, actual_resp); - assert!(str::contains(actual_req, expected_req)); - assert!(str::contains(actual_resp, expected_resp)); - } - - pub fn impl_tcp_socket_impl_reader_handles_eof() { - use core::io::{Reader,ReaderUtil}; - - let hl_loop = &uv::global_loop::get(); - let server_ip = "127.0.0.1"; - let server_port = 10041u; - let expected_req = ~"GET /"; - let expected_resp = "A string\nwith multiple lines\n"; - - let (cont_po, cont_ch) = stream::<()>(); - let cont_ch = SharedChan::new(cont_ch); - // server - let hl_loop_clone = hl_loop.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - let cont_ch = cont_ch.clone(); - run_tcp_test_server( - server_ip, - server_port, - expected_resp.to_str(), - cont_ch.clone(), - &hl_loop_clone); - }; - cont_po.recv(); - // client - debug!("server started, firing up client.."); - let server_addr = ip::v4::parse_addr(server_ip); - let conn_result = connect(server_addr, server_port, hl_loop); - if result::is_err(&conn_result) { - assert!(false); - } - let sock_buf = @socket_buf(result::unwrap(conn_result)); - buf_write(sock_buf, expected_req); - - let buf_reader = sock_buf as @Reader; - let actual_response = str::from_bytes(buf_reader.read_whole_stream()); - debug!("Actual response: %s", actual_response); - assert!(expected_resp == actual_response); - } - - fn buf_write<W:io::Writer>(w: &W, val: &str) { - debug!("BUF_WRITE: val len %?", str::len(val)); - do str::byte_slice(val) |b_slice| { - debug!("BUF_WRITE: b_slice len %?", - b_slice.len()); - w.write(b_slice) - } - } - - fn buf_read<R:io::Reader>(r: &R, len: uint) -> ~str { - let new_bytes = (*r).read_bytes(len); - debug!("in buf_read.. new_bytes len: %?", - new_bytes.len()); - str::from_bytes(new_bytes) - } - - fn run_tcp_test_server(server_ip: &str, server_port: uint, resp: ~str, - cont_ch: SharedChan<()>, - iotask: &IoTask) -> ~str { - let (server_po, server_ch) = stream::<~str>(); - let server_ch = SharedChan::new(server_ch); - let server_ip_addr = ip::v4::parse_addr(server_ip); - let resp_cell = Cell(resp); - let listen_result = listen(server_ip_addr, server_port, 128, - iotask, - // on_establish_cb -- called when listener is set up - |kill_ch| { - debug!("establish_cb %?", - kill_ch); - cont_ch.send(()); - }, - // risky to run this on the loop, but some users - // will want the POWER - |new_conn, kill_ch| { - let resp_cell2 = Cell(resp_cell.take()); - debug!("SERVER: new connection!"); - let (cont_po, cont_ch) = stream(); - let server_ch = server_ch.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - debug!("SERVER: starting worker for new req"); - - let accept_result = accept(new_conn); - debug!("SERVER: after accept()"); - if result::is_err(&accept_result) { - debug!("SERVER: error accept connection"); - let err_data = result::get_err(&accept_result); - kill_ch.send(Some(err_data)); - debug!( - "SERVER/WORKER: send on err cont ch"); - cont_ch.send(()); - } - else { - debug!("SERVER/WORKER: send on cont ch"); - cont_ch.send(()); - let sock = result::unwrap(accept_result); - let peer_addr = sock.get_peer_addr(); - debug!("SERVER: successfully accepted \ - connection from %s:%u", - ip::format_addr(&peer_addr), - ip::get_port(&peer_addr)); - let received_req_bytes = read(&sock, 0u); - match received_req_bytes { - result::Ok(data) => { - debug!("SERVER: got REQ str::from_bytes.."); - debug!("SERVER: REQ data len: %?", - data.len()); - server_ch.send( - str::from_bytes(data)); - debug!("SERVER: before write"); - tcp_write_single(&sock, str::to_bytes(resp_cell2.take())); - debug!("SERVER: after write.. die"); - kill_ch.send(None); - } - result::Err(err_data) => { - debug!("SERVER: error recvd: %s %s", - err_data.err_name, err_data.err_msg); - kill_ch.send(Some(err_data)); - server_ch.send(~""); - } - } - debug!("SERVER: worker spinning down"); - } - } - debug!("SERVER: waiting to recv on cont_ch"); - cont_po.recv(); - }); - // err check on listen_result - if result::is_err(&listen_result) { - match result::get_err(&listen_result) { - GenericListenErr(ref name, ref msg) => { - fail!("SERVER: exited abnormally name %s msg %s", *name, *msg); - } - AccessDenied => { - fail!("SERVER: exited abnormally, got access denied.."); - } - AddressInUse => { - fail!("SERVER: exited abnormally, got address in use..."); - } - } - } - let ret_val = server_po.recv(); - debug!("SERVER: exited and got return val: '%s'", ret_val); - ret_val - } - - fn run_tcp_test_server_fail(server_ip: &str, server_port: uint, - iotask: &IoTask) -> TcpListenErrData { - let server_ip_addr = ip::v4::parse_addr(server_ip); - let listen_result = listen(server_ip_addr, server_port, 128, - iotask, - // on_establish_cb -- called when listener is set up - |kill_ch| { - debug!("establish_cb %?", kill_ch); - }, - |new_conn, kill_ch| { - fail!("SERVER: shouldn't be called.. %? %?", new_conn, kill_ch); - }); - // err check on listen_result - if result::is_err(&listen_result) { - result::get_err(&listen_result) - } - else { - fail!("SERVER: did not fail as expected") - } - } - - fn run_tcp_test_client(server_ip: &str, server_port: uint, resp: &str, - iotask: &IoTask) -> result::Result<~str, - TcpConnectErrData> { - let server_ip_addr = ip::v4::parse_addr(server_ip); - - debug!("CLIENT: starting.."); - let connect_result = connect(server_ip_addr, server_port, - iotask); - if result::is_err(&connect_result) { - debug!("CLIENT: failed to connect"); - let err_data = result::get_err(&connect_result); - Err(err_data) - } - else { - let sock = result::unwrap(connect_result); - let resp_bytes = str::to_bytes(resp); - tcp_write_single(&sock, resp_bytes); - let read_result = sock.read(0u); - if read_result.is_err() { - debug!("CLIENT: failure to read"); - Ok(~"") - } - else { - let ret_val = str::from_bytes(read_result.get()); - debug!("CLIENT: after client_ch recv ret: '%s'", - ret_val); - Ok(ret_val) - } - } - } - - fn tcp_write_single(sock: &TcpSocket, val: ~[u8]) { - let mut write_result_future = sock.write_future(val); - let write_result = write_result_future.get(); - if result::is_err(&write_result) { - debug!("tcp_write_single: write failed!"); - let err_data = result::get_err(&write_result); - debug!("tcp_write_single err name: %s msg: %s", - err_data.err_name, err_data.err_msg); - // meh. torn on what to do here. - fail!("tcp_write_single failed"); - } - } -} diff --git a/src/libstd/net_url.rs b/src/libstd/net_url.rs deleted file mode 100644 index fb57c717be9..00000000000 --- a/src/libstd/net_url.rs +++ /dev/null @@ -1,1068 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Types/fns concerning URLs (see RFC 3986) - -use core::cmp::Eq; -use core::io::{Reader, ReaderUtil}; -use core::io; -use core::hashmap::HashMap; -use core::str; -use core::to_bytes; -use core::uint; - -#[deriving(Clone, Eq)] -struct Url { - scheme: ~str, - user: Option<UserInfo>, - host: ~str, - port: Option<~str>, - path: ~str, - query: Query, - fragment: Option<~str> -} - -#[deriving(Clone, Eq)] -struct UserInfo { - user: ~str, - pass: Option<~str> -} - -pub type Query = ~[(~str, ~str)]; - -pub impl Url { - fn new( - scheme: ~str, - user: Option<UserInfo>, - host: ~str, - port: Option<~str>, - path: ~str, - query: Query, - fragment: Option<~str> - ) -> Url { - Url { - scheme: scheme, - user: user, - host: host, - port: port, - path: path, - query: query, - fragment: fragment, - } - } -} - -pub impl UserInfo { - fn new(user: ~str, pass: Option<~str>) -> UserInfo { - UserInfo { user: user, pass: pass } - } -} - -fn encode_inner(s: &str, full_url: bool) -> ~str { - do io::with_str_reader(s) |rdr| { - let mut out = ~""; - - while !rdr.eof() { - let ch = rdr.read_byte() as char; - match ch { - // unreserved: - 'A' .. 'Z' | - 'a' .. 'z' | - '0' .. '9' | - '-' | '.' | '_' | '~' => { - str::push_char(&mut out, ch); - } - _ => { - if full_url { - match ch { - // gen-delims: - ':' | '/' | '?' | '#' | '[' | ']' | '@' | - - // sub-delims: - '!' | '$' | '&' | '"' | '(' | ')' | '*' | - '+' | ',' | ';' | '=' => { - str::push_char(&mut out, ch); - } - - _ => out += fmt!("%%%X", ch as uint) - } - } else { - out += fmt!("%%%X", ch as uint); - } - } - } - } - - out - } -} - -/** - * Encodes a URI by replacing reserved characters with percent encoded - * character sequences. - * - * This function is compliant with RFC 3986. - */ -pub fn encode(s: &str) -> ~str { - encode_inner(s, true) -} - -/** - * Encodes a URI component by replacing reserved characters with percent - * encoded character sequences. - * - * This function is compliant with RFC 3986. - */ - -pub fn encode_component(s: &str) -> ~str { - encode_inner(s, false) -} - -fn decode_inner(s: &str, full_url: bool) -> ~str { - do io::with_str_reader(s) |rdr| { - let mut out = ~""; - - while !rdr.eof() { - match rdr.read_char() { - '%' => { - let bytes = rdr.read_bytes(2u); - let ch = uint::parse_bytes(bytes, 16u).get() as char; - - if full_url { - // Only decode some characters: - match ch { - // gen-delims: - ':' | '/' | '?' | '#' | '[' | ']' | '@' | - - // sub-delims: - '!' | '$' | '&' | '"' | '(' | ')' | '*' | - '+' | ',' | ';' | '=' => { - str::push_char(&mut out, '%'); - str::push_char(&mut out, bytes[0u] as char); - str::push_char(&mut out, bytes[1u] as char); - } - - ch => str::push_char(&mut out, ch) - } - } else { - str::push_char(&mut out, ch); - } - } - ch => str::push_char(&mut out, ch) - } - } - - out - } -} - -/** - * Decode a string encoded with percent encoding. - * - * This will only decode escape sequences generated by encode. - */ -pub fn decode(s: &str) -> ~str { - decode_inner(s, true) -} - -/** - * Decode a string encoded with percent encoding. - */ -pub fn decode_component(s: &str) -> ~str { - decode_inner(s, false) -} - -fn encode_plus(s: &str) -> ~str { - do io::with_str_reader(s) |rdr| { - let mut out = ~""; - - while !rdr.eof() { - let ch = rdr.read_byte() as char; - match ch { - 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '.' | '-' => { - str::push_char(&mut out, ch); - } - ' ' => str::push_char(&mut out, '+'), - _ => out += fmt!("%%%X", ch as uint) - } - } - - out - } -} - -/** - * Encode a hashmap to the 'application/x-www-form-urlencoded' media type. - */ -pub fn encode_form_urlencoded(m: &HashMap<~str, ~[~str]>) -> ~str { - let mut out = ~""; - let mut first = true; - - for m.each |key, values| { - let key = encode_plus(*key); - - for values.each |value| { - if first { - first = false; - } else { - str::push_char(&mut out, '&'); - first = false; - } - - out += fmt!("%s=%s", key, encode_plus(*value)); - } - } - - out -} - -/** - * Decode a string encoded with the 'application/x-www-form-urlencoded' media - * type into a hashmap. - */ -pub fn decode_form_urlencoded(s: &[u8]) -> HashMap<~str, ~[~str]> { - do io::with_bytes_reader(s) |rdr| { - let mut m = HashMap::new(); - let mut key = ~""; - let mut value = ~""; - let mut parsing_key = true; - - while !rdr.eof() { - match rdr.read_char() { - '&' | ';' => { - if key != ~"" && value != ~"" { - let mut values = match m.pop(&key) { - Some(values) => values, - None => ~[], - }; - - values.push(value); - m.insert(key, values); - } - - parsing_key = true; - key = ~""; - value = ~""; - } - '=' => parsing_key = false, - ch => { - let ch = match ch { - '%' => { - let bytes = rdr.read_bytes(2u); - uint::parse_bytes(bytes, 16u).get() as char - } - '+' => ' ', - ch => ch - }; - - if parsing_key { - str::push_char(&mut key, ch) - } else { - str::push_char(&mut value, ch) - } - } - } - } - - if key != ~"" && value != ~"" { - let mut values = match m.pop(&key) { - Some(values) => values, - None => ~[], - }; - - values.push(value); - m.insert(key, values); - } - - m - } -} - - -fn split_char_first(s: &str, c: char) -> (~str, ~str) { - let len = str::len(s); - let mut index = len; - let mut mat = 0; - do io::with_str_reader(s) |rdr| { - let mut ch; - while !rdr.eof() { - ch = rdr.read_byte() as char; - if ch == c { - // found a match, adjust markers - index = rdr.tell()-1; - mat = 1; - break; - } - } - } - if index+mat == len { - return (str::slice(s, 0, index).to_owned(), ~""); - } else { - return (str::slice(s, 0, index).to_owned(), - str::slice(s, index + mat, str::len(s)).to_owned()); - } -} - -fn userinfo_from_str(uinfo: &str) -> UserInfo { - let (user, p) = split_char_first(uinfo, ':'); - let pass = if str::len(p) == 0 { - None - } else { - Some(p) - }; - return UserInfo::new(user, pass); -} - -fn userinfo_to_str(userinfo: &UserInfo) -> ~str { - match userinfo.pass { - Some(ref pass) => fmt!("%s:%s@", userinfo.user, *pass), - None => fmt!("%s@", userinfo.user), - } -} - -fn query_from_str(rawquery: &str) -> Query { - let mut query: Query = ~[]; - if str::len(rawquery) != 0 { - for str::each_split_char(rawquery, '&') |p| { - let (k, v) = split_char_first(p, '='); - query.push((decode_component(k), decode_component(v))); - }; - } - return query; -} - -pub fn query_to_str(query: &Query) -> ~str { - let mut strvec = ~[]; - for query.each |kv| { - match kv { - &(ref k, ref v) => { - strvec.push(fmt!("%s=%s", - encode_component(*k), - encode_component(*v)) - ); - } - } - } - return str::connect(strvec, "&"); -} - -// returns the scheme and the rest of the url, or a parsing error -pub fn get_scheme(rawurl: &str) -> Result<(~str, ~str), ~str> { - for str::each_chari(rawurl) |i,c| { - match c { - 'A' .. 'Z' | 'a' .. 'z' => loop, - '0' .. '9' | '+' | '-' | '.' => { - if i == 0 { - return Err(~"url: Scheme must begin with a letter."); - } - loop; - } - ':' => { - if i == 0 { - return Err(~"url: Scheme cannot be empty."); - } else { - return Ok((rawurl.slice(0,i).to_owned(), - rawurl.slice(i+1,str::len(rawurl)).to_owned())); - } - } - _ => { - return Err(~"url: Invalid character in scheme."); - } - } - }; - return Err(~"url: Scheme must be terminated with a colon."); -} - -#[deriving(Clone, Eq)] -enum Input { - Digit, // all digits - Hex, // digits and letters a-f - Unreserved // all other legal characters -} - -// returns userinfo, host, port, and unparsed part, or an error -fn get_authority(rawurl: &str) -> - Result<(Option<UserInfo>, ~str, Option<~str>, ~str), ~str> { - if !str::starts_with(rawurl, "//") { - // there is no authority. - return Ok((None, ~"", None, rawurl.to_str())); - } - - enum State { - Start, // starting state - PassHostPort, // could be in user or port - Ip6Port, // either in ipv6 host or port - Ip6Host, // are in an ipv6 host - InHost, // are in a host - may be ipv6, but don't know yet - InPort // are in port - } - - let len = rawurl.len(); - let mut st = Start; - let mut in = Digit; // most restricted, start here. - - let mut userinfo = None; - let mut host = ~""; - let mut port = None; - - let mut colon_count = 0; - let mut pos = 0, begin = 2, end = len; - - for str::each_chari(rawurl) |i,c| { - if i < 2 { loop; } // ignore the leading // - - // deal with input class first - match c { - '0' .. '9' => (), - 'A' .. 'F' | 'a' .. 'f' => { - if in == Digit { - in = Hex; - } - } - 'G' .. 'Z' | 'g' .. 'z' | '-' | '.' | '_' | '~' | '%' | - '&' |'\'' | '(' | ')' | '+' | '!' | '*' | ',' | ';' | '=' => { - in = Unreserved; - } - ':' | '@' | '?' | '#' | '/' => { - // separators, don't change anything - } - _ => { - return Err(~"Illegal character in authority"); - } - } - - // now process states - match c { - ':' => { - colon_count += 1; - match st { - Start => { - pos = i; - st = PassHostPort; - } - PassHostPort => { - // multiple colons means ipv6 address. - if in == Unreserved { - return Err( - ~"Illegal characters in IPv6 address."); - } - st = Ip6Host; - } - InHost => { - pos = i; - // can't be sure whether this is an ipv6 address or a port - if in == Unreserved { - return Err(~"Illegal characters in authority."); - } - st = Ip6Port; - } - Ip6Port => { - if in == Unreserved { - return Err(~"Illegal characters in authority."); - } - st = Ip6Host; - } - Ip6Host => { - if colon_count > 7 { - host = str::slice(rawurl, begin, i).to_owned(); - pos = i; - st = InPort; - } - } - _ => { - return Err(~"Invalid ':' in authority."); - } - } - in = Digit; // reset input class - } - - '@' => { - in = Digit; // reset input class - colon_count = 0; // reset count - match st { - Start => { - let user = str::slice(rawurl, begin, i).to_owned(); - userinfo = Some(UserInfo::new(user, None)); - st = InHost; - } - PassHostPort => { - let user = str::slice(rawurl, begin, pos).to_owned(); - let pass = str::slice(rawurl, pos+1, i).to_owned(); - userinfo = Some(UserInfo::new(user, Some(pass))); - st = InHost; - } - _ => { - return Err(~"Invalid '@' in authority."); - } - } - begin = i+1; - } - - '?' | '#' | '/' => { - end = i; - break; - } - _ => () - } - end = i; - } - - let end = end; // make end immutable so it can be captured - - let host_is_end_plus_one: &fn() -> bool = || { - end+1 == len - && !['?', '#', '/'].contains(&(rawurl[end] as char)) - }; - - // finish up - match st { - Start => { - if host_is_end_plus_one() { - host = str::slice(rawurl, begin, end+1).to_owned(); - } else { - host = str::slice(rawurl, begin, end).to_owned(); - } - } - PassHostPort | Ip6Port => { - if in != Digit { - return Err(~"Non-digit characters in port."); - } - host = str::slice(rawurl, begin, pos).to_owned(); - port = Some(str::slice(rawurl, pos+1, end).to_owned()); - } - Ip6Host | InHost => { - host = str::slice(rawurl, begin, end).to_owned(); - } - InPort => { - if in != Digit { - return Err(~"Non-digit characters in port."); - } - port = Some(str::slice(rawurl, pos+1, end).to_owned()); - } - } - - let rest = if host_is_end_plus_one() { ~"" } - else { str::slice(rawurl, end, len).to_owned() }; - return Ok((userinfo, host, port, rest)); -} - - -// returns the path and unparsed part of url, or an error -fn get_path(rawurl: &str, authority: bool) -> - Result<(~str, ~str), ~str> { - let len = str::len(rawurl); - let mut end = len; - for str::each_chari(rawurl) |i,c| { - match c { - 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '&' |'\'' | '(' | ')' | '.' - | '@' | ':' | '%' | '/' | '+' | '!' | '*' | ',' | ';' | '=' - | '_' | '-' => { - loop; - } - '?' | '#' => { - end = i; - break; - } - _ => return Err(~"Invalid character in path.") - } - } - - if authority { - if end != 0 && !str::starts_with(rawurl, "/") { - return Err(~"Non-empty path must begin with\ - '/' in presence of authority."); - } - } - - return Ok((decode_component(str::slice(rawurl, 0, end).to_owned()), - str::slice(rawurl, end, len).to_owned())); -} - -// returns the parsed query and the fragment, if present -fn get_query_fragment(rawurl: &str) -> - Result<(Query, Option<~str>), ~str> { - if !str::starts_with(rawurl, "?") { - if str::starts_with(rawurl, "#") { - let f = decode_component(str::slice(rawurl, - 1, - str::len(rawurl)).to_owned()); - return Ok((~[], Some(f))); - } else { - return Ok((~[], None)); - } - } - let (q, r) = split_char_first(str::slice(rawurl, 1, - str::len(rawurl)).to_owned(), '#'); - let f = if str::len(r) != 0 { - Some(decode_component(r)) } else { None }; - return Ok((query_from_str(q), f)); -} - -/** - * Parse a `str` to a `url` - * - * # Arguments - * - * `rawurl` - a string representing a full url, including scheme. - * - * # Returns - * - * a `url` that contains the parsed representation of the url. - * - */ - -pub fn from_str(rawurl: &str) -> Result<Url, ~str> { - // scheme - let (scheme, rest) = match get_scheme(rawurl) { - Ok(val) => val, - Err(e) => return Err(e), - }; - - // authority - let (userinfo, host, port, rest) = match get_authority(rest) { - Ok(val) => val, - Err(e) => return Err(e), - }; - - // path - let has_authority = if host == ~"" { false } else { true }; - let (path, rest) = match get_path(rest, has_authority) { - Ok(val) => val, - Err(e) => return Err(e), - }; - - // query and fragment - let (query, fragment) = match get_query_fragment(rest) { - Ok(val) => val, - Err(e) => return Err(e), - }; - - Ok(Url::new(scheme, userinfo, host, port, path, query, fragment)) -} - -impl FromStr for Url { - fn from_str(s: &str) -> Option<Url> { - match from_str(s) { - Ok(url) => Some(url), - Err(_) => None - } - } -} - -/** - * Format a `url` as a string - * - * # Arguments - * - * `url` - a url. - * - * # Returns - * - * a `str` that contains the formatted url. Note that this will usually - * be an inverse of `from_str` but might strip out unneeded separators. - * for example, "http://somehost.com?", when parsed and formatted, will - * result in just "http://somehost.com". - * - */ -pub fn to_str(url: &Url) -> ~str { - let user = match url.user { - Some(ref user) => userinfo_to_str(user), - None => ~"", - }; - - let authority = if url.host.is_empty() { - ~"" - } else { - fmt!("//%s%s", user, url.host) - }; - - let query = if url.query.is_empty() { - ~"" - } else { - fmt!("?%s", query_to_str(&url.query)) - }; - - let fragment = match url.fragment { - Some(ref fragment) => fmt!("#%s", encode_component(*fragment)), - None => ~"", - }; - - fmt!("%s:%s%s%s%s", url.scheme, authority, url.path, query, fragment) -} - -impl ToStr for Url { - pub fn to_str(&self) -> ~str { - to_str(self) - } -} - -impl IterBytes for Url { - fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool { - self.to_str().iter_bytes(lsb0, f) - } -} - -// Put a few tests outside of the 'test' module so they can test the internal -// functions and those functions don't need 'pub' - -#[test] -fn test_split_char_first() { - let (u,v) = split_char_first(~"hello, sweet world", ','); - assert_eq!(u, ~"hello"); - assert_eq!(v, ~" sweet world"); - - let (u,v) = split_char_first(~"hello sweet world", ','); - assert_eq!(u, ~"hello sweet world"); - assert_eq!(v, ~""); -} - -#[test] -fn test_get_authority() { - let (u, h, p, r) = get_authority( - "//user:pass@rust-lang.org/something").unwrap(); - assert_eq!(u, Some(UserInfo::new(~"user", Some(~"pass")))); - assert_eq!(h, ~"rust-lang.org"); - assert!(p.is_none()); - assert_eq!(r, ~"/something"); - - let (u, h, p, r) = get_authority( - "//rust-lang.org:8000?something").unwrap(); - assert!(u.is_none()); - assert_eq!(h, ~"rust-lang.org"); - assert_eq!(p, Some(~"8000")); - assert_eq!(r, ~"?something"); - - let (u, h, p, r) = get_authority( - "//rust-lang.org#blah").unwrap(); - assert!(u.is_none()); - assert_eq!(h, ~"rust-lang.org"); - assert!(p.is_none()); - assert_eq!(r, ~"#blah"); - - // ipv6 tests - let (_, h, _, _) = get_authority( - "//2001:0db8:85a3:0042:0000:8a2e:0370:7334#blah").unwrap(); - assert_eq!(h, ~"2001:0db8:85a3:0042:0000:8a2e:0370:7334"); - - let (_, h, p, _) = get_authority( - "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000#blah").unwrap(); - assert_eq!(h, ~"2001:0db8:85a3:0042:0000:8a2e:0370:7334"); - assert_eq!(p, Some(~"8000")); - - let (u, h, p, _) = get_authority( - "//us:p@2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000#blah" - ).unwrap(); - assert_eq!(u, Some(UserInfo::new(~"us", Some(~"p")))); - assert_eq!(h, ~"2001:0db8:85a3:0042:0000:8a2e:0370:7334"); - assert_eq!(p, Some(~"8000")); - - // invalid authorities; - assert!(get_authority("//user:pass@rust-lang:something").is_err()); - assert!(get_authority("//user@rust-lang:something:/path").is_err()); - assert!(get_authority( - "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:800a").is_err()); - assert!(get_authority( - "//2001:0db8:85a3:0042:0000:8a2e:0370:7334:8000:00").is_err()); - - // these parse as empty, because they don't start with '//' - let (_, h, _, _) = get_authority(~"user:pass@rust-lang").unwrap(); - assert_eq!(h, ~""); - let (_, h, _, _) = get_authority(~"rust-lang.org").unwrap(); - assert_eq!(h, ~""); -} - -#[test] -fn test_get_path() { - let (p, r) = get_path("/something+%20orother", true).unwrap(); - assert_eq!(p, ~"/something+ orother"); - assert_eq!(r, ~""); - let (p, r) = get_path("test@email.com#fragment", false).unwrap(); - assert_eq!(p, ~"test@email.com"); - assert_eq!(r, ~"#fragment"); - let (p, r) = get_path(~"/gen/:addr=?q=v", false).unwrap(); - assert_eq!(p, ~"/gen/:addr="); - assert_eq!(r, ~"?q=v"); - - //failure cases - assert!(get_path(~"something?q", true).is_err()); -} - -#[cfg(test)] -mod tests { - - use net_url::*; - - use core::hashmap::HashMap; - - #[test] - fn test_url_parse() { - let url = ~"http://user:pass@rust-lang.org/doc?s=v#something"; - - let up = from_str(url); - let u = up.unwrap(); - assert!(u.scheme == ~"http"); - let userinfo = u.user.get_ref(); - assert!(userinfo.user == ~"user"); - assert!(userinfo.pass.get_ref() == &~"pass"); - assert!(u.host == ~"rust-lang.org"); - assert!(u.path == ~"/doc"); - assert!(u.query == ~[(~"s", ~"v")]); - assert!(u.fragment.get_ref() == &~"something"); - } - - #[test] - fn test_url_parse_host_slash() { - let urlstr = ~"http://0.42.42.42/"; - let url = from_str(urlstr).unwrap(); - assert!(url.host == ~"0.42.42.42"); - assert!(url.path == ~"/"); - } - - #[test] - fn test_url_with_underscores() { - let urlstr = ~"http://dotcom.com/file_name.html"; - let url = from_str(urlstr).unwrap(); - assert!(url.path == ~"/file_name.html"); - } - - #[test] - fn test_url_with_dashes() { - let urlstr = ~"http://dotcom.com/file-name.html"; - let url = from_str(urlstr).unwrap(); - assert!(url.path == ~"/file-name.html"); - } - - #[test] - fn test_no_scheme() { - assert!(get_scheme("noschemehere.html").is_err()); - } - - #[test] - fn test_invalid_scheme_errors() { - assert!(from_str("99://something").is_err()); - assert!(from_str("://something").is_err()); - } - - #[test] - fn test_full_url_parse_and_format() { - let url = ~"http://user:pass@rust-lang.org/doc?s=v#something"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_userless_url_parse_and_format() { - let url = ~"http://rust-lang.org/doc?s=v#something"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_queryless_url_parse_and_format() { - let url = ~"http://user:pass@rust-lang.org/doc#something"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_empty_query_url_parse_and_format() { - let url = ~"http://user:pass@rust-lang.org/doc?#something"; - let should_be = ~"http://user:pass@rust-lang.org/doc#something"; - assert_eq!(from_str(url).unwrap().to_str(), should_be); - } - - #[test] - fn test_fragmentless_url_parse_and_format() { - let url = ~"http://user:pass@rust-lang.org/doc?q=v"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_minimal_url_parse_and_format() { - let url = ~"http://rust-lang.org/doc"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_scheme_host_only_url_parse_and_format() { - let url = ~"http://rust-lang.org"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_pathless_url_parse_and_format() { - let url = ~"http://user:pass@rust-lang.org?q=v#something"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_scheme_host_fragment_only_url_parse_and_format() { - let url = ~"http://rust-lang.org#something"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_url_component_encoding() { - let url = ~"http://rust-lang.org/doc%20uments?ba%25d%20=%23%26%2B"; - let u = from_str(url).unwrap(); - assert!(u.path == ~"/doc uments"); - assert!(u.query == ~[(~"ba%d ", ~"#&+")]); - } - - #[test] - fn test_url_without_authority() { - let url = ~"mailto:test@email.com"; - assert_eq!(from_str(url).unwrap().to_str(), url); - } - - #[test] - fn test_encode() { - assert_eq!(encode(""), ~""); - assert_eq!(encode("http://example.com"), ~"http://example.com"); - assert_eq!(encode("foo bar% baz"), ~"foo%20bar%25%20baz"); - assert_eq!(encode(" "), ~"%20"); - assert_eq!(encode("!"), ~"!"); - assert_eq!(encode("\""), ~"\""); - assert_eq!(encode("#"), ~"#"); - assert_eq!(encode("$"), ~"$"); - assert_eq!(encode("%"), ~"%25"); - assert_eq!(encode("&"), ~"&"); - assert_eq!(encode("'"), ~"%27"); - assert_eq!(encode("("), ~"("); - assert_eq!(encode(")"), ~")"); - assert_eq!(encode("*"), ~"*"); - assert_eq!(encode("+"), ~"+"); - assert_eq!(encode(","), ~","); - assert_eq!(encode("/"), ~"/"); - assert_eq!(encode(":"), ~":"); - assert_eq!(encode(";"), ~";"); - assert_eq!(encode("="), ~"="); - assert_eq!(encode("?"), ~"?"); - assert_eq!(encode("@"), ~"@"); - assert_eq!(encode("["), ~"["); - assert_eq!(encode("]"), ~"]"); - } - - #[test] - fn test_encode_component() { - assert_eq!(encode_component(""), ~""); - assert!(encode_component("http://example.com") == - ~"http%3A%2F%2Fexample.com"); - assert!(encode_component("foo bar% baz") == - ~"foo%20bar%25%20baz"); - assert_eq!(encode_component(" "), ~"%20"); - assert_eq!(encode_component("!"), ~"%21"); - assert_eq!(encode_component("#"), ~"%23"); - assert_eq!(encode_component("$"), ~"%24"); - assert_eq!(encode_component("%"), ~"%25"); - assert_eq!(encode_component("&"), ~"%26"); - assert_eq!(encode_component("'"), ~"%27"); - assert_eq!(encode_component("("), ~"%28"); - assert_eq!(encode_component(")"), ~"%29"); - assert_eq!(encode_component("*"), ~"%2A"); - assert_eq!(encode_component("+"), ~"%2B"); - assert_eq!(encode_component(","), ~"%2C"); - assert_eq!(encode_component("/"), ~"%2F"); - assert_eq!(encode_component(":"), ~"%3A"); - assert_eq!(encode_component(";"), ~"%3B"); - assert_eq!(encode_component("="), ~"%3D"); - assert_eq!(encode_component("?"), ~"%3F"); - assert_eq!(encode_component("@"), ~"%40"); - assert_eq!(encode_component("["), ~"%5B"); - assert_eq!(encode_component("]"), ~"%5D"); - } - - #[test] - fn test_decode() { - assert_eq!(decode(""), ~""); - assert_eq!(decode("abc/def 123"), ~"abc/def 123"); - assert_eq!(decode("abc%2Fdef%20123"), ~"abc%2Fdef 123"); - assert_eq!(decode("%20"), ~" "); - assert_eq!(decode("%21"), ~"%21"); - assert_eq!(decode("%22"), ~"%22"); - assert_eq!(decode("%23"), ~"%23"); - assert_eq!(decode("%24"), ~"%24"); - assert_eq!(decode("%25"), ~"%"); - assert_eq!(decode("%26"), ~"%26"); - assert_eq!(decode("%27"), ~"'"); - assert_eq!(decode("%28"), ~"%28"); - assert_eq!(decode("%29"), ~"%29"); - assert_eq!(decode("%2A"), ~"%2A"); - assert_eq!(decode("%2B"), ~"%2B"); - assert_eq!(decode("%2C"), ~"%2C"); - assert_eq!(decode("%2F"), ~"%2F"); - assert_eq!(decode("%3A"), ~"%3A"); - assert_eq!(decode("%3B"), ~"%3B"); - assert_eq!(decode("%3D"), ~"%3D"); - assert_eq!(decode("%3F"), ~"%3F"); - assert_eq!(decode("%40"), ~"%40"); - assert_eq!(decode("%5B"), ~"%5B"); - assert_eq!(decode("%5D"), ~"%5D"); - } - - #[test] - fn test_decode_component() { - assert_eq!(decode_component(""), ~""); - assert_eq!(decode_component("abc/def 123"), ~"abc/def 123"); - assert_eq!(decode_component("abc%2Fdef%20123"), ~"abc/def 123"); - assert_eq!(decode_component("%20"), ~" "); - assert_eq!(decode_component("%21"), ~"!"); - assert_eq!(decode_component("%22"), ~"\""); - assert_eq!(decode_component("%23"), ~"#"); - assert_eq!(decode_component("%24"), ~"$"); - assert_eq!(decode_component("%25"), ~"%"); - assert_eq!(decode_component("%26"), ~"&"); - assert_eq!(decode_component("%27"), ~"'"); - assert_eq!(decode_component("%28"), ~"("); - assert_eq!(decode_component("%29"), ~")"); - assert_eq!(decode_component("%2A"), ~"*"); - assert_eq!(decode_component("%2B"), ~"+"); - assert_eq!(decode_component("%2C"), ~","); - assert_eq!(decode_component("%2F"), ~"/"); - assert_eq!(decode_component("%3A"), ~":"); - assert_eq!(decode_component("%3B"), ~";"); - assert_eq!(decode_component("%3D"), ~"="); - assert_eq!(decode_component("%3F"), ~"?"); - assert_eq!(decode_component("%40"), ~"@"); - assert_eq!(decode_component("%5B"), ~"["); - assert_eq!(decode_component("%5D"), ~"]"); - } - - #[test] - fn test_encode_form_urlencoded() { - let mut m = HashMap::new(); - assert_eq!(encode_form_urlencoded(&m), ~""); - - m.insert(~"", ~[]); - m.insert(~"foo", ~[]); - assert_eq!(encode_form_urlencoded(&m), ~""); - - let mut m = HashMap::new(); - m.insert(~"foo", ~[~"bar", ~"123"]); - assert_eq!(encode_form_urlencoded(&m), ~"foo=bar&foo=123"); - - let mut m = HashMap::new(); - m.insert(~"foo bar", ~[~"abc", ~"12 = 34"]); - assert!(encode_form_urlencoded(&m) == - ~"foo+bar=abc&foo+bar=12+%3D+34"); - } - - #[test] - fn test_decode_form_urlencoded() { - // FIXME #4449: Commented out because this causes an ICE, but only - // on FreeBSD - /* - assert_eq!(decode_form_urlencoded(~[]).len(), 0); - - let s = str::to_bytes("a=1&foo+bar=abc&foo+bar=12+%3D+34"); - let form = decode_form_urlencoded(s); - assert_eq!(form.len(), 2); - assert_eq!(form.get_ref(&~"a"), &~[~"1"]); - assert_eq!(form.get_ref(&~"foo bar"), &~[~"abc", ~"12 = 34"]); - */ - } -} diff --git a/src/libstd/nil.rs b/src/libstd/nil.rs new file mode 100644 index 00000000000..833bd3459ce --- /dev/null +++ b/src/libstd/nil.rs @@ -0,0 +1,50 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Functions for the unit type. + +*/ + +#[cfg(not(test))] +use prelude::*; + +#[cfg(not(test))] +impl Eq for () { + #[inline(always)] + fn eq(&self, _other: &()) -> bool { true } + #[inline(always)] + fn ne(&self, _other: &()) -> bool { false } +} + +#[cfg(not(test))] +impl Ord for () { + #[inline(always)] + fn lt(&self, _other: &()) -> bool { false } + #[inline(always)] + fn le(&self, _other: &()) -> bool { true } + #[inline(always)] + fn ge(&self, _other: &()) -> bool { true } + #[inline(always)] + fn gt(&self, _other: &()) -> bool { false } +} + +#[cfg(not(test))] +impl TotalOrd for () { + #[inline(always)] + fn cmp(&self, _other: &()) -> Ordering { Equal } +} + +#[cfg(not(test))] +impl TotalEq for () { + #[inline(always)] + fn equals(&self, _other: &()) -> bool { true } +} diff --git a/src/libstd/num/bigint.rs b/src/libstd/num/bigint.rs deleted file mode 100644 index 21685b03c07..00000000000 --- a/src/libstd/num/bigint.rs +++ /dev/null @@ -1,2021 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - -A Big integer (signed version: BigInt, unsigned version: BigUint). - -A BigUint is represented as an array of BigDigits. -A BigInt is a combination of BigUint and Sign. -*/ - -use core::cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater}; -use core::num::{IntConvertible, Zero, One, ToStrRadix, FromStrRadix, Orderable}; - -/** -A BigDigit is a BigUint's composing element. - -A BigDigit is half the size of machine word size. -*/ -#[cfg(target_arch = "x86")] -#[cfg(target_arch = "arm")] -#[cfg(target_arch = "mips")] -pub type BigDigit = u16; - -/** -A BigDigit is a BigUint's composing element. - -A BigDigit is half the size of machine word size. -*/ -#[cfg(target_arch = "x86_64")] -pub type BigDigit = u32; - -pub mod BigDigit { - use bigint::BigDigit; - - #[cfg(target_arch = "x86")] - #[cfg(target_arch = "arm")] - #[cfg(target_arch = "mips")] - pub static bits: uint = 16; - - #[cfg(target_arch = "x86_64")] - pub static bits: uint = 32; - - pub static base: uint = 1 << bits; - priv static hi_mask: uint = (-1 as uint) << bits; - priv static lo_mask: uint = (-1 as uint) >> bits; - - #[inline(always)] - priv fn get_hi(n: uint) -> BigDigit { (n >> bits) as BigDigit } - #[inline(always)] - priv fn get_lo(n: uint) -> BigDigit { (n & lo_mask) as BigDigit } - - /// Split one machine sized unsigned integer into two BigDigits. - #[inline(always)] - pub fn from_uint(n: uint) -> (BigDigit, BigDigit) { - (get_hi(n), get_lo(n)) - } - - /// Join two BigDigits into one machine sized unsigned integer - #[inline(always)] - pub fn to_uint(hi: BigDigit, lo: BigDigit) -> uint { - (lo as uint) | ((hi as uint) << bits) - } -} - -/** -A big unsigned integer type. - -A BigUint-typed value BigUint { data: @[a, b, c] } represents a number -(a + b * BigDigit::base + c * BigDigit::base^2). -*/ -#[deriving(Clone)] -pub struct BigUint { - priv data: ~[BigDigit] -} - -impl Eq for BigUint { - #[inline(always)] - fn eq(&self, other: &BigUint) -> bool { self.equals(other) } - #[inline(always)] - fn ne(&self, other: &BigUint) -> bool { !self.equals(other) } -} - -impl TotalEq for BigUint { - #[inline(always)] - fn equals(&self, other: &BigUint) -> bool { - match self.cmp(other) { Equal => true, _ => false } - } -} - -impl Ord for BigUint { - #[inline(always)] - fn lt(&self, other: &BigUint) -> bool { - match self.cmp(other) { Less => true, _ => false} - } - #[inline(always)] - fn le(&self, other: &BigUint) -> bool { - match self.cmp(other) { Less | Equal => true, _ => false } - } - #[inline(always)] - fn ge(&self, other: &BigUint) -> bool { - match self.cmp(other) { Greater | Equal => true, _ => false } - } - #[inline(always)] - fn gt(&self, other: &BigUint) -> bool { - match self.cmp(other) { Greater => true, _ => false } - } -} - -impl TotalOrd for BigUint { - #[inline(always)] - fn cmp(&self, other: &BigUint) -> Ordering { - let s_len = self.data.len(), o_len = other.data.len(); - if s_len < o_len { return Less; } - if s_len > o_len { return Greater; } - - for self.data.eachi_reverse |i, elm| { - match (*elm, other.data[i]) { - (l, r) if l < r => return Less, - (l, r) if l > r => return Greater, - _ => loop - }; - } - return Equal; - } -} - -impl ToStr for BigUint { - #[inline(always)] - fn to_str(&self) -> ~str { self.to_str_radix(10) } -} - -impl FromStr for BigUint { - #[inline(always)] - fn from_str(s: &str) -> Option<BigUint> { - FromStrRadix::from_str_radix(s, 10) - } -} - -impl Num for BigUint {} - -impl Orderable for BigUint { - #[inline(always)] - fn min(&self, other: &BigUint) -> BigUint { - if self < other { self.clone() } else { other.clone() } - } - - #[inline(always)] - fn max(&self, other: &BigUint) -> BigUint { - if self > other { self.clone() } else { other.clone() } - } - - #[inline(always)] - fn clamp(&self, mn: &BigUint, mx: &BigUint) -> BigUint { - if self > mx { mx.clone() } else - if self < mn { mn.clone() } else { self.clone() } - } -} - -impl Shl<uint, BigUint> for BigUint { - #[inline(always)] - fn shl(&self, rhs: &uint) -> BigUint { - let n_unit = *rhs / BigDigit::bits; - let n_bits = *rhs % BigDigit::bits; - return self.shl_unit(n_unit).shl_bits(n_bits); - } -} - -impl Shr<uint, BigUint> for BigUint { - #[inline(always)] - fn shr(&self, rhs: &uint) -> BigUint { - let n_unit = *rhs / BigDigit::bits; - let n_bits = *rhs % BigDigit::bits; - return self.shr_unit(n_unit).shr_bits(n_bits); - } -} - -impl Zero for BigUint { - #[inline(always)] - fn zero() -> BigUint { BigUint::new(~[]) } - - #[inline(always)] - fn is_zero(&self) -> bool { self.data.is_empty() } -} - -impl One for BigUint { - #[inline(always)] - fn one() -> BigUint { BigUint::new(~[1]) } -} - -impl Unsigned for BigUint {} - -impl Add<BigUint, BigUint> for BigUint { - #[inline(always)] - fn add(&self, other: &BigUint) -> BigUint { - let new_len = uint::max(self.data.len(), other.data.len()); - - let mut carry = 0; - let sum = do vec::from_fn(new_len) |i| { - let ai = if i < self.data.len() { self.data[i] } else { 0 }; - let bi = if i < other.data.len() { other.data[i] } else { 0 }; - let (hi, lo) = BigDigit::from_uint( - (ai as uint) + (bi as uint) + (carry as uint) - ); - carry = hi; - lo - }; - if carry == 0 { return BigUint::new(sum) }; - return BigUint::new(sum + [carry]); - } -} - -impl Sub<BigUint, BigUint> for BigUint { - #[inline(always)] - fn sub(&self, other: &BigUint) -> BigUint { - let new_len = uint::max(self.data.len(), other.data.len()); - - let mut borrow = 0; - let diff = do vec::from_fn(new_len) |i| { - let ai = if i < self.data.len() { self.data[i] } else { 0 }; - let bi = if i < other.data.len() { other.data[i] } else { 0 }; - let (hi, lo) = BigDigit::from_uint( - (BigDigit::base) + - (ai as uint) - (bi as uint) - (borrow as uint) - ); - /* - hi * (base) + lo == 1*(base) + ai - bi - borrow - => ai - bi - borrow < 0 <=> hi == 0 - */ - borrow = if hi == 0 { 1 } else { 0 }; - lo - }; - - assert_eq!(borrow, 0); // <=> assert!((self >= other)); - return BigUint::new(diff); - } -} - -impl Mul<BigUint, BigUint> for BigUint { - fn mul(&self, other: &BigUint) -> BigUint { - if self.is_zero() || other.is_zero() { return Zero::zero(); } - - let s_len = self.data.len(), o_len = other.data.len(); - if s_len == 1 { return mul_digit(other, self.data[0]); } - if o_len == 1 { return mul_digit(self, other.data[0]); } - - // Using Karatsuba multiplication - // (a1 * base + a0) * (b1 * base + b0) - // = a1*b1 * base^2 + - // (a1*b1 + a0*b0 - (a1-b0)*(b1-a0)) * base + - // a0*b0 - let half_len = uint::max(s_len, o_len) / 2; - let (sHi, sLo) = cut_at(self, half_len); - let (oHi, oLo) = cut_at(other, half_len); - - let ll = sLo * oLo; - let hh = sHi * oHi; - let mm = { - let (s1, n1) = sub_sign(sHi, sLo); - let (s2, n2) = sub_sign(oHi, oLo); - match (s1, s2) { - (Equal, _) | (_, Equal) => hh + ll, - (Less, Greater) | (Greater, Less) => hh + ll + (n1 * n2), - (Less, Less) | (Greater, Greater) => hh + ll - (n1 * n2) - } - }; - - return ll + mm.shl_unit(half_len) + hh.shl_unit(half_len * 2); - - #[inline(always)] - fn mul_digit(a: &BigUint, n: BigDigit) -> BigUint { - if n == 0 { return Zero::zero(); } - if n == 1 { return copy *a; } - - let mut carry = 0; - let prod = do vec::map(a.data) |ai| { - let (hi, lo) = BigDigit::from_uint( - (*ai as uint) * (n as uint) + (carry as uint) - ); - carry = hi; - lo - }; - if carry == 0 { return BigUint::new(prod) }; - return BigUint::new(prod + [carry]); - } - - #[inline(always)] - fn cut_at(a: &BigUint, n: uint) -> (BigUint, BigUint) { - let mid = uint::min(a.data.len(), n); - return (BigUint::from_slice(vec::slice(a.data, mid, - a.data.len())), - BigUint::from_slice(vec::slice(a.data, 0, mid))); - } - - #[inline(always)] - fn sub_sign(a: BigUint, b: BigUint) -> (Ordering, BigUint) { - match a.cmp(&b) { - Less => (Less, b - a), - Greater => (Greater, a - b), - _ => (Equal, Zero::zero()) - } - } - } -} - -impl Div<BigUint, BigUint> for BigUint { - #[inline(always)] - fn div(&self, other: &BigUint) -> BigUint { - let (q, _) = self.div_rem(other); - return q; - } -} - -impl Rem<BigUint, BigUint> for BigUint { - #[inline(always)] - fn rem(&self, other: &BigUint) -> BigUint { - let (_, r) = self.div_rem(other); - return r; - } -} - -impl Neg<BigUint> for BigUint { - #[inline(always)] - fn neg(&self) -> BigUint { fail!() } -} - -impl Integer for BigUint { - #[inline(always)] - fn div_rem(&self, other: &BigUint) -> (BigUint, BigUint) { - self.div_mod_floor(other) - } - - #[inline(always)] - fn div_floor(&self, other: &BigUint) -> BigUint { - let (d, _) = self.div_mod_floor(other); - return d; - } - - #[inline(always)] - fn mod_floor(&self, other: &BigUint) -> BigUint { - let (_, m) = self.div_mod_floor(other); - return m; - } - - #[inline(always)] - fn div_mod_floor(&self, other: &BigUint) -> (BigUint, BigUint) { - if other.is_zero() { fail!() } - if self.is_zero() { return (Zero::zero(), Zero::zero()); } - if *other == One::one() { return (copy *self, Zero::zero()); } - - match self.cmp(other) { - Less => return (Zero::zero(), copy *self), - Equal => return (One::one(), Zero::zero()), - Greater => {} // Do nothing - } - - let mut shift = 0; - let mut n = *other.data.last(); - while n < (1 << BigDigit::bits - 2) { - n <<= 1; - shift += 1; - } - assert!(shift < BigDigit::bits); - let (d, m) = div_mod_floor_inner(self << shift, other << shift); - return (d, m >> shift); - - #[inline(always)] - fn div_mod_floor_inner(a: BigUint, b: BigUint) -> (BigUint, BigUint) { - let mut m = a; - let mut d = Zero::zero::<BigUint>(); - let mut n = 1; - while m >= b { - let mut (d0, d_unit, b_unit) = div_estimate(&m, &b, n); - let mut prod = b * d0; - while prod > m { - // FIXME(#6050): overloaded operators force moves with generic types - // d0 -= d_unit - d0 = d0 - d_unit; - // FIXME(#6050): overloaded operators force moves with generic types - // prod = prod - b_unit; - prod = prod - b_unit - } - if d0.is_zero() { - n = 2; - loop; - } - n = 1; - // FIXME(#6102): Assignment operator for BigInt causes ICE - // d += d0; - d = d + d0; - // FIXME(#6102): Assignment operator for BigInt causes ICE - // m -= prod; - m = m - prod; - } - return (d, m); - } - - #[inline(always)] - fn div_estimate(a: &BigUint, b: &BigUint, n: uint) - -> (BigUint, BigUint, BigUint) { - if a.data.len() < n { - return (Zero::zero(), Zero::zero(), copy *a); - } - - let an = vec::slice(a.data, a.data.len() - n, a.data.len()); - let bn = *b.data.last(); - let mut d = ~[]; - let mut carry = 0; - for an.each_reverse |elt| { - let ai = BigDigit::to_uint(carry, *elt); - let di = ai / (bn as uint); - assert!(di < BigDigit::base); - carry = (ai % (bn as uint)) as BigDigit; - d = ~[di as BigDigit] + d; - } - - let shift = (a.data.len() - an.len()) - (b.data.len() - 1); - if shift == 0 { - return (BigUint::new(d), One::one(), copy *b); - } - return (BigUint::from_slice(d).shl_unit(shift), - One::one::<BigUint>().shl_unit(shift), - b.shl_unit(shift)); - } - } - - /** - * Calculates the Greatest Common Divisor (GCD) of the number and `other` - * - * The result is always positive - */ - #[inline(always)] - fn gcd(&self, other: &BigUint) -> BigUint { - // Use Euclid's algorithm - let mut m = copy *self, n = copy *other; - while !m.is_zero() { - let temp = m; - m = n % temp; - n = temp; - } - return n; - } - - /** - * Calculates the Lowest Common Multiple (LCM) of the number and `other` - */ - #[inline(always)] - fn lcm(&self, other: &BigUint) -> BigUint { ((*self * *other) / self.gcd(other)) } - - /// Returns `true` if the number can be divided by `other` without leaving a remainder - #[inline(always)] - fn is_multiple_of(&self, other: &BigUint) -> bool { (*self % *other).is_zero() } - - /// Returns `true` if the number is divisible by `2` - #[inline(always)] - fn is_even(&self) -> bool { - // Considering only the last digit. - if self.data.is_empty() { - true - } else { - self.data[0].is_even() - } - } - - /// Returns `true` if the number is not divisible by `2` - #[inline(always)] - fn is_odd(&self) -> bool { !self.is_even() } -} - -impl IntConvertible for BigUint { - #[inline(always)] - fn to_int(&self) -> int { - uint::min(self.to_uint(), int::max_value as uint) as int - } - - #[inline(always)] - fn from_int(n: int) -> BigUint { - if (n < 0) { Zero::zero() } else { BigUint::from_uint(n as uint) } - } -} - -impl ToStrRadix for BigUint { - #[inline(always)] - fn to_str_radix(&self, radix: uint) -> ~str { - assert!(1 < radix && radix <= 16); - let (base, max_len) = get_radix_base(radix); - if base == BigDigit::base { - return fill_concat(self.data, radix, max_len) - } - return fill_concat(convert_base(copy *self, base), radix, max_len); - - #[inline(always)] - fn convert_base(n: BigUint, base: uint) -> ~[BigDigit] { - let divider = BigUint::from_uint(base); - let mut result = ~[]; - let mut m = n; - while m > divider { - let (d, m0) = m.div_mod_floor(÷r); - result += [m0.to_uint() as BigDigit]; - m = d; - } - if !m.is_zero() { - result += [m.to_uint() as BigDigit]; - } - return result; - } - - #[inline(always)] - fn fill_concat(v: &[BigDigit], radix: uint, l: uint) -> ~str { - if v.is_empty() { return ~"0" } - let s = str::concat(vec::reversed(v).map(|n| { - let s = uint::to_str_radix(*n as uint, radix); - str::from_chars(vec::from_elem(l - s.len(), '0')) + s - })); - str::trim_left_chars(s, ['0']).to_owned() - } - } -} - -impl FromStrRadix for BigUint { - /// Creates and initializes an BigUint. - #[inline(always)] - pub fn from_str_radix(s: &str, radix: uint) - -> Option<BigUint> { - BigUint::parse_bytes(str::to_bytes(s), radix) - } -} - -impl BigUint { - /// Creates and initializes an BigUint. - #[inline(always)] - pub fn new(v: ~[BigDigit]) -> BigUint { - // omit trailing zeros - let new_len = v.rposition(|n| *n != 0).map_default(0, |p| *p + 1); - - if new_len == v.len() { return BigUint { data: v }; } - let mut v = v; - v.truncate(new_len); - return BigUint { data: v }; - } - - /// Creates and initializes an BigUint. - #[inline(always)] - pub fn from_uint(n: uint) -> BigUint { - match BigDigit::from_uint(n) { - (0, 0) => Zero::zero(), - (0, n0) => BigUint::new(~[n0]), - (n1, n0) => BigUint::new(~[n0, n1]) - } - } - - /// Creates and initializes an BigUint. - #[inline(always)] - pub fn from_slice(slice: &[BigDigit]) -> BigUint { - return BigUint::new(vec::to_owned(slice)); - } - - /// Creates and initializes an BigUint. - #[inline(always)] - pub fn parse_bytes(buf: &[u8], radix: uint) - -> Option<BigUint> { - let (base, unit_len) = get_radix_base(radix); - let base_num: BigUint = BigUint::from_uint(base); - - let mut end = buf.len(); - let mut n: BigUint = Zero::zero(); - let mut power: BigUint = One::one(); - loop { - let start = uint::max(end, unit_len) - unit_len; - match uint::parse_bytes(vec::slice(buf, start, end), radix) { - // FIXME(#6102): Assignment operator for BigInt causes ICE - // Some(d) => n += BigUint::from_uint(d) * power, - Some(d) => n = n + BigUint::from_uint(d) * power, - None => return None - } - if end <= unit_len { - return Some(n); - } - end -= unit_len; - // FIXME(#6050): overloaded operators force moves with generic types - // power *= base_num; - power = power * base_num; - } - } - - #[inline(always)] - pub fn to_uint(&self) -> uint { - match self.data.len() { - 0 => 0, - 1 => self.data[0] as uint, - 2 => BigDigit::to_uint(self.data[1], self.data[0]), - _ => uint::max_value - } - } - - #[inline(always)] - priv fn shl_unit(&self, n_unit: uint) -> BigUint { - if n_unit == 0 || self.is_zero() { return copy *self; } - - return BigUint::new(vec::from_elem(n_unit, 0) + self.data); - } - - #[inline(always)] - priv fn shl_bits(&self, n_bits: uint) -> BigUint { - if n_bits == 0 || self.is_zero() { return copy *self; } - - let mut carry = 0; - let shifted = do vec::map(self.data) |elem| { - let (hi, lo) = BigDigit::from_uint( - (*elem as uint) << n_bits | (carry as uint) - ); - carry = hi; - lo - }; - if carry == 0 { return BigUint::new(shifted); } - return BigUint::new(shifted + [carry]); - } - - #[inline(always)] - priv fn shr_unit(&self, n_unit: uint) -> BigUint { - if n_unit == 0 { return copy *self; } - if self.data.len() < n_unit { return Zero::zero(); } - return BigUint::from_slice( - vec::slice(self.data, n_unit, self.data.len()) - ); - } - - #[inline(always)] - priv fn shr_bits(&self, n_bits: uint) -> BigUint { - if n_bits == 0 || self.data.is_empty() { return copy *self; } - - let mut borrow = 0; - let mut shifted = ~[]; - for self.data.each_reverse |elem| { - shifted = ~[(*elem >> n_bits) | borrow] + shifted; - borrow = *elem << (BigDigit::bits - n_bits); - } - return BigUint::new(shifted); - } -} - -#[cfg(target_arch = "x86_64")] -#[inline(always)] -priv fn get_radix_base(radix: uint) -> (uint, uint) { - assert!(1 < radix && radix <= 16); - match radix { - 2 => (4294967296, 32), - 3 => (3486784401, 20), - 4 => (4294967296, 16), - 5 => (1220703125, 13), - 6 => (2176782336, 12), - 7 => (1977326743, 11), - 8 => (1073741824, 10), - 9 => (3486784401, 10), - 10 => (1000000000, 9), - 11 => (2357947691, 9), - 12 => (429981696, 8), - 13 => (815730721, 8), - 14 => (1475789056, 8), - 15 => (2562890625, 8), - 16 => (4294967296, 8), - _ => fail!() - } -} - -#[cfg(target_arch = "arm")] -#[cfg(target_arch = "x86")] -#[cfg(target_arch = "mips")] -#[inline(always)] -priv fn get_radix_base(radix: uint) -> (uint, uint) { - assert!(1 < radix && radix <= 16); - match radix { - 2 => (65536, 16), - 3 => (59049, 10), - 4 => (65536, 8), - 5 => (15625, 6), - 6 => (46656, 6), - 7 => (16807, 5), - 8 => (32768, 5), - 9 => (59049, 5), - 10 => (10000, 4), - 11 => (14641, 4), - 12 => (20736, 4), - 13 => (28561, 4), - 14 => (38416, 4), - 15 => (50625, 4), - 16 => (65536, 4), - _ => fail!() - } -} - -/// A Sign is a BigInt's composing element. -#[deriving(Eq, Clone)] -pub enum Sign { Minus, Zero, Plus } - -impl Ord for Sign { - #[inline(always)] - fn lt(&self, other: &Sign) -> bool { - match self.cmp(other) { Less => true, _ => false} - } - #[inline(always)] - fn le(&self, other: &Sign) -> bool { - match self.cmp(other) { Less | Equal => true, _ => false } - } - #[inline(always)] - fn ge(&self, other: &Sign) -> bool { - match self.cmp(other) { Greater | Equal => true, _ => false } - } - #[inline(always)] - fn gt(&self, other: &Sign) -> bool { - match self.cmp(other) { Greater => true, _ => false } - } -} - -impl TotalOrd for Sign { - #[inline(always)] - fn cmp(&self, other: &Sign) -> Ordering { - match (*self, *other) { - (Minus, Minus) | (Zero, Zero) | (Plus, Plus) => Equal, - (Minus, Zero) | (Minus, Plus) | (Zero, Plus) => Less, - _ => Greater - } - } -} - -impl Neg<Sign> for Sign { - /// Negate Sign value. - #[inline(always)] - fn neg(&self) -> Sign { - match *self { - Minus => Plus, - Zero => Zero, - Plus => Minus - } - } -} - -/// A big signed integer type. -#[deriving(Clone)] -pub struct BigInt { - priv sign: Sign, - priv data: BigUint -} - -impl Eq for BigInt { - #[inline(always)] - fn eq(&self, other: &BigInt) -> bool { self.equals(other) } - #[inline(always)] - fn ne(&self, other: &BigInt) -> bool { !self.equals(other) } -} - -impl TotalEq for BigInt { - #[inline(always)] - fn equals(&self, other: &BigInt) -> bool { - match self.cmp(other) { Equal => true, _ => false } - } -} - -impl Ord for BigInt { - #[inline(always)] - fn lt(&self, other: &BigInt) -> bool { - match self.cmp(other) { Less => true, _ => false} - } - #[inline(always)] - fn le(&self, other: &BigInt) -> bool { - match self.cmp(other) { Less | Equal => true, _ => false } - } - #[inline(always)] - fn ge(&self, other: &BigInt) -> bool { - match self.cmp(other) { Greater | Equal => true, _ => false } - } - #[inline(always)] - fn gt(&self, other: &BigInt) -> bool { - match self.cmp(other) { Greater => true, _ => false } - } -} - -impl TotalOrd for BigInt { - #[inline(always)] - fn cmp(&self, other: &BigInt) -> Ordering { - let scmp = self.sign.cmp(&other.sign); - if scmp != Equal { return scmp; } - - match self.sign { - Zero => Equal, - Plus => self.data.cmp(&other.data), - Minus => other.data.cmp(&self.data), - } - } -} - -impl ToStr for BigInt { - #[inline(always)] - fn to_str(&self) -> ~str { self.to_str_radix(10) } -} - -impl FromStr for BigInt { - #[inline(always)] - fn from_str(s: &str) -> Option<BigInt> { - FromStrRadix::from_str_radix(s, 10) - } -} - -impl Num for BigInt {} - -impl Orderable for BigInt { - #[inline(always)] - fn min(&self, other: &BigInt) -> BigInt { - if self < other { self.clone() } else { other.clone() } - } - - #[inline(always)] - fn max(&self, other: &BigInt) -> BigInt { - if self > other { self.clone() } else { other.clone() } - } - - #[inline(always)] - fn clamp(&self, mn: &BigInt, mx: &BigInt) -> BigInt { - if self > mx { mx.clone() } else - if self < mn { mn.clone() } else { self.clone() } - } -} - -impl Shl<uint, BigInt> for BigInt { - #[inline(always)] - fn shl(&self, rhs: &uint) -> BigInt { - BigInt::from_biguint(self.sign, self.data << *rhs) - } -} - -impl Shr<uint, BigInt> for BigInt { - #[inline(always)] - fn shr(&self, rhs: &uint) -> BigInt { - BigInt::from_biguint(self.sign, self.data >> *rhs) - } -} - -impl Zero for BigInt { - #[inline(always)] - fn zero() -> BigInt { - BigInt::from_biguint(Zero, Zero::zero()) - } - - #[inline(always)] - fn is_zero(&self) -> bool { self.sign == Zero } -} - -impl One for BigInt { - #[inline(always)] - fn one() -> BigInt { - BigInt::from_biguint(Plus, One::one()) - } -} - -impl Signed for BigInt { - #[inline(always)] - fn abs(&self) -> BigInt { - match self.sign { - Plus | Zero => self.clone(), - Minus => BigInt::from_biguint(Plus, self.data.clone()) - } - } - - #[inline(always)] - fn abs_sub(&self, other: &BigInt) -> BigInt { - if *self <= *other { Zero::zero() } else { *self - *other } - } - - #[inline(always)] - fn signum(&self) -> BigInt { - match self.sign { - Plus => BigInt::from_biguint(Plus, One::one()), - Minus => BigInt::from_biguint(Minus, One::one()), - Zero => Zero::zero(), - } - } - - #[inline(always)] - fn is_positive(&self) -> bool { self.sign == Plus } - - #[inline(always)] - fn is_negative(&self) -> bool { self.sign == Minus } -} - -impl Add<BigInt, BigInt> for BigInt { - #[inline(always)] - fn add(&self, other: &BigInt) -> BigInt { - match (self.sign, other.sign) { - (Zero, _) => other.clone(), - (_, Zero) => self.clone(), - (Plus, Plus) => BigInt::from_biguint(Plus, - self.data + other.data), - (Plus, Minus) => self - (-*other), - (Minus, Plus) => other - (-*self), - (Minus, Minus) => -((-self) + (-*other)) - } - } -} - -impl Sub<BigInt, BigInt> for BigInt { - #[inline(always)] - fn sub(&self, other: &BigInt) -> BigInt { - match (self.sign, other.sign) { - (Zero, _) => -other, - (_, Zero) => self.clone(), - (Plus, Plus) => match self.data.cmp(&other.data) { - Less => BigInt::from_biguint(Minus, other.data - self.data), - Greater => BigInt::from_biguint(Plus, self.data - other.data), - Equal => Zero::zero() - }, - (Plus, Minus) => self + (-*other), - (Minus, Plus) => -((-self) + *other), - (Minus, Minus) => (-other) - (-*self) - } - } -} - -impl Mul<BigInt, BigInt> for BigInt { - #[inline(always)] - fn mul(&self, other: &BigInt) -> BigInt { - match (self.sign, other.sign) { - (Zero, _) | (_, Zero) => Zero::zero(), - (Plus, Plus) | (Minus, Minus) => { - BigInt::from_biguint(Plus, self.data * other.data) - }, - (Plus, Minus) | (Minus, Plus) => { - BigInt::from_biguint(Minus, self.data * other.data) - } - } - } -} - -impl Div<BigInt, BigInt> for BigInt { - #[inline(always)] - fn div(&self, other: &BigInt) -> BigInt { - let (q, _) = self.div_rem(other); - return q; - } -} - -impl Rem<BigInt, BigInt> for BigInt { - #[inline(always)] - fn rem(&self, other: &BigInt) -> BigInt { - let (_, r) = self.div_rem(other); - return r; - } -} - -impl Neg<BigInt> for BigInt { - #[inline(always)] - fn neg(&self) -> BigInt { - BigInt::from_biguint(self.sign.neg(), self.data.clone()) - } -} - -impl Integer for BigInt { - #[inline(always)] - fn div_rem(&self, other: &BigInt) -> (BigInt, BigInt) { - // r.sign == self.sign - let (d_ui, r_ui) = self.data.div_mod_floor(&other.data); - let d = BigInt::from_biguint(Plus, d_ui); - let r = BigInt::from_biguint(Plus, r_ui); - match (self.sign, other.sign) { - (_, Zero) => fail!(), - (Plus, Plus) | (Zero, Plus) => ( d, r), - (Plus, Minus) | (Zero, Minus) => (-d, r), - (Minus, Plus) => (-d, -r), - (Minus, Minus) => ( d, -r) - } - } - - #[inline(always)] - fn div_floor(&self, other: &BigInt) -> BigInt { - let (d, _) = self.div_mod_floor(other); - return d; - } - - #[inline(always)] - fn mod_floor(&self, other: &BigInt) -> BigInt { - let (_, m) = self.div_mod_floor(other); - return m; - } - - #[inline(always)] - fn div_mod_floor(&self, other: &BigInt) -> (BigInt, BigInt) { - // m.sign == other.sign - let (d_ui, m_ui) = self.data.div_rem(&other.data); - let d = BigInt::from_biguint(Plus, d_ui), - m = BigInt::from_biguint(Plus, m_ui); - match (self.sign, other.sign) { - (_, Zero) => fail!(), - (Plus, Plus) | (Zero, Plus) => (d, m), - (Plus, Minus) | (Zero, Minus) => if m.is_zero() { - (-d, Zero::zero()) - } else { - (-d - One::one(), m + *other) - }, - (Minus, Plus) => if m.is_zero() { - (-d, Zero::zero()) - } else { - (-d - One::one(), other - m) - }, - (Minus, Minus) => (d, -m) - } - } - - /** - * Calculates the Greatest Common Divisor (GCD) of the number and `other` - * - * The result is always positive - */ - #[inline(always)] - fn gcd(&self, other: &BigInt) -> BigInt { - BigInt::from_biguint(Plus, self.data.gcd(&other.data)) - } - - /** - * Calculates the Lowest Common Multiple (LCM) of the number and `other` - */ - #[inline(always)] - fn lcm(&self, other: &BigInt) -> BigInt { - BigInt::from_biguint(Plus, self.data.lcm(&other.data)) - } - - /// Returns `true` if the number can be divided by `other` without leaving a remainder - #[inline(always)] - fn is_multiple_of(&self, other: &BigInt) -> bool { self.data.is_multiple_of(&other.data) } - - /// Returns `true` if the number is divisible by `2` - #[inline(always)] - fn is_even(&self) -> bool { self.data.is_even() } - - /// Returns `true` if the number is not divisible by `2` - #[inline(always)] - fn is_odd(&self) -> bool { self.data.is_odd() } -} - -impl IntConvertible for BigInt { - #[inline(always)] - fn to_int(&self) -> int { - match self.sign { - Plus => uint::min(self.to_uint(), int::max_value as uint) as int, - Zero => 0, - Minus => uint::min((-self).to_uint(), - (int::max_value as uint) + 1) as int - } - } - - #[inline(always)] - fn from_int(n: int) -> BigInt { - if n > 0 { - return BigInt::from_biguint(Plus, BigUint::from_uint(n as uint)); - } - if n < 0 { - return BigInt::from_biguint( - Minus, BigUint::from_uint(uint::max_value - (n as uint) + 1) - ); - } - return Zero::zero(); - } -} - -impl ToStrRadix for BigInt { - #[inline(always)] - fn to_str_radix(&self, radix: uint) -> ~str { - match self.sign { - Plus => self.data.to_str_radix(radix), - Zero => ~"0", - Minus => ~"-" + self.data.to_str_radix(radix) - } - } -} - -impl FromStrRadix for BigInt { - /// Creates and initializes an BigInt. - #[inline(always)] - fn from_str_radix(s: &str, radix: uint) - -> Option<BigInt> { - BigInt::parse_bytes(str::to_bytes(s), radix) - } -} - -pub impl BigInt { - /// Creates and initializes an BigInt. - #[inline(always)] - pub fn new(sign: Sign, v: ~[BigDigit]) -> BigInt { - BigInt::from_biguint(sign, BigUint::new(v)) - } - - /// Creates and initializes an BigInt. - #[inline(always)] - pub fn from_biguint(sign: Sign, data: BigUint) -> BigInt { - if sign == Zero || data.is_zero() { - return BigInt { sign: Zero, data: Zero::zero() }; - } - return BigInt { sign: sign, data: data }; - } - - /// Creates and initializes an BigInt. - #[inline(always)] - pub fn from_uint(n: uint) -> BigInt { - if n == 0 { return Zero::zero(); } - return BigInt::from_biguint(Plus, BigUint::from_uint(n)); - } - - /// Creates and initializes an BigInt. - #[inline(always)] - pub fn from_slice(sign: Sign, slice: &[BigDigit]) -> BigInt { - BigInt::from_biguint(sign, BigUint::from_slice(slice)) - } - - /// Creates and initializes an BigInt. - #[inline(always)] - pub fn parse_bytes(buf: &[u8], radix: uint) - -> Option<BigInt> { - if buf.is_empty() { return None; } - let mut sign = Plus; - let mut start = 0; - if buf[0] == ('-' as u8) { - sign = Minus; - start = 1; - } - return BigUint::parse_bytes(vec::slice(buf, start, buf.len()), radix) - .map_consume(|bu| BigInt::from_biguint(sign, bu)); - } - - #[inline(always)] - fn to_uint(&self) -> uint { - match self.sign { - Plus => self.data.to_uint(), - Zero => 0, - Minus => 0 - } - } -} - -#[cfg(test)] -mod biguint_tests { - use super::*; - use core::num::{IntConvertible, Zero, One, FromStrRadix}; - use core::cmp::{Less, Equal, Greater}; - - #[test] - fn test_from_slice() { - fn check(slice: &[BigDigit], data: &[BigDigit]) { - assert!(data == BigUint::from_slice(slice).data); - } - check(~[1], ~[1]); - check(~[0, 0, 0], ~[]); - check(~[1, 2, 0, 0], ~[1, 2]); - check(~[0, 0, 1, 2], ~[0, 0, 1, 2]); - check(~[0, 0, 1, 2, 0, 0], ~[0, 0, 1, 2]); - check(~[-1], ~[-1]); - } - - #[test] - fn test_cmp() { - let data = [ &[], &[1], &[2], &[-1], &[0, 1], &[2, 1], &[1, 1, 1] ] - .map(|v| BigUint::from_slice(*v)); - for data.eachi |i, ni| { - for vec::slice(data, i, data.len()).eachi |j0, nj| { - let j = j0 + i; - if i == j { - assert_eq!(ni.cmp(nj), Equal); - assert_eq!(nj.cmp(ni), Equal); - assert_eq!(ni, nj); - assert!(!(ni != nj)); - assert!(ni <= nj); - assert!(ni >= nj); - assert!(!(ni < nj)); - assert!(!(ni > nj)); - } else { - assert_eq!(ni.cmp(nj), Less); - assert_eq!(nj.cmp(ni), Greater); - - assert!(!(ni == nj)); - assert!(ni != nj); - - assert!(ni <= nj); - assert!(!(ni >= nj)); - assert!(ni < nj); - assert!(!(ni > nj)); - - assert!(!(nj <= ni)); - assert!(nj >= ni); - assert!(!(nj < ni)); - assert!(nj > ni); - } - } - } - } - - #[test] - fn test_shl() { - fn check(v: ~[BigDigit], shift: uint, ans: ~[BigDigit]) { - assert_eq!(BigUint::new(v) << shift, BigUint::new(ans)); - } - - check(~[], 3, ~[]); - check(~[1, 1, 1], 3, ~[1 << 3, 1 << 3, 1 << 3]); - check(~[1 << (BigDigit::bits - 2)], 2, ~[0, 1]); - check(~[1 << (BigDigit::bits - 2)], 3, ~[0, 2]); - check(~[1 << (BigDigit::bits - 2)], 3 + BigDigit::bits, ~[0, 0, 2]); - - test_shl_bits(); - - #[cfg(target_arch = "x86_64")] - fn test_shl_bits() { - check(~[0x7654_3210, 0xfedc_ba98, - 0x7654_3210, 0xfedc_ba98], 4, - ~[0x6543_2100, 0xedcb_a987, - 0x6543_210f, 0xedcb_a987, 0xf]); - check(~[0x2222_1111, 0x4444_3333, - 0x6666_5555, 0x8888_7777], 16, - ~[0x1111_0000, 0x3333_2222, - 0x5555_4444, 0x7777_6666, 0x8888]); - } - - #[cfg(target_arch = "arm")] - #[cfg(target_arch = "x86")] - #[cfg(target_arch = "mips")] - fn test_shl_bits() { - check(~[0x3210, 0x7654, 0xba98, 0xfedc, - 0x3210, 0x7654, 0xba98, 0xfedc], 4, - ~[0x2100, 0x6543, 0xa987, 0xedcb, - 0x210f, 0x6543, 0xa987, 0xedcb, 0xf]); - check(~[0x1111, 0x2222, 0x3333, 0x4444, - 0x5555, 0x6666, 0x7777, 0x8888], 16, - ~[0x0000, 0x1111, 0x2222, 0x3333, - 0x4444, 0x5555, 0x6666, 0x7777, 0x8888]); - } - - } - - #[test] - #[ignore(cfg(target_arch = "x86"))] - #[ignore(cfg(target_arch = "arm"))] - #[ignore(cfg(target_arch = "mips"))] - fn test_shr() { - fn check(v: ~[BigDigit], shift: uint, ans: ~[BigDigit]) { - assert_eq!(BigUint::new(v) >> shift, BigUint::new(ans)); - } - - check(~[], 3, ~[]); - check(~[1, 1, 1], 3, - ~[1 << (BigDigit::bits - 3), 1 << (BigDigit::bits - 3)]); - check(~[1 << 2], 2, ~[1]); - check(~[1, 2], 3, ~[1 << (BigDigit::bits - 2)]); - check(~[1, 1, 2], 3 + BigDigit::bits, ~[1 << (BigDigit::bits - 2)]); - check(~[0, 1], 1, ~[0x80000000]); - test_shr_bits(); - - #[cfg(target_arch = "x86_64")] - fn test_shr_bits() { - check(~[0x6543_2100, 0xedcb_a987, - 0x6543_210f, 0xedcb_a987, 0xf], 4, - ~[0x7654_3210, 0xfedc_ba98, - 0x7654_3210, 0xfedc_ba98]); - check(~[0x1111_0000, 0x3333_2222, - 0x5555_4444, 0x7777_6666, 0x8888], 16, - ~[0x2222_1111, 0x4444_3333, - 0x6666_5555, 0x8888_7777]); - } - - #[cfg(target_arch = "arm")] - #[cfg(target_arch = "x86")] - #[cfg(target_arch = "mips")] - fn test_shr_bits() { - check(~[0x2100, 0x6543, 0xa987, 0xedcb, - 0x210f, 0x6543, 0xa987, 0xedcb, 0xf], 4, - ~[0x3210, 0x7654, 0xba98, 0xfedc, - 0x3210, 0x7654, 0xba98, 0xfedc]); - check(~[0x0000, 0x1111, 0x2222, 0x3333, - 0x4444, 0x5555, 0x6666, 0x7777, 0x8888], 16, - ~[0x1111, 0x2222, 0x3333, 0x4444, - 0x5555, 0x6666, 0x7777, 0x8888]); - } - } - - #[test] - fn test_convert_int() { - fn check(v: ~[BigDigit], i: int) { - let b = BigUint::new(v); - assert!(b == IntConvertible::from_int(i)); - assert!(b.to_int() == i); - } - - check(~[], 0); - check(~[1], 1); - check(~[-1], (uint::max_value >> BigDigit::bits) as int); - check(~[ 0, 1], ((uint::max_value >> BigDigit::bits) + 1) as int); - check(~[-1, -1 >> 1], int::max_value); - - assert_eq!(BigUint::new(~[0, -1]).to_int(), int::max_value); - assert_eq!(BigUint::new(~[0, 0, 1]).to_int(), int::max_value); - assert_eq!(BigUint::new(~[0, 0, -1]).to_int(), int::max_value); - } - - #[test] - fn test_convert_uint() { - fn check(v: ~[BigDigit], u: uint) { - let b = BigUint::new(v); - assert!(b == BigUint::from_uint(u)); - assert!(b.to_uint() == u); - } - - check(~[], 0); - check(~[ 1], 1); - check(~[-1], uint::max_value >> BigDigit::bits); - check(~[ 0, 1], (uint::max_value >> BigDigit::bits) + 1); - check(~[ 0, -1], uint::max_value << BigDigit::bits); - check(~[-1, -1], uint::max_value); - - assert_eq!(BigUint::new(~[0, 0, 1]).to_uint(), uint::max_value); - assert_eq!(BigUint::new(~[0, 0, -1]).to_uint(), uint::max_value); - } - - static sum_triples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] = &[ - (&[], &[], &[]), - (&[], &[ 1], &[ 1]), - (&[ 1], &[ 1], &[ 2]), - (&[ 1], &[ 1, 1], &[ 2, 1]), - (&[ 1], &[-1], &[ 0, 1]), - (&[ 1], &[-1, -1], &[ 0, 0, 1]), - (&[-1, -1], &[-1, -1], &[-2, -1, 1]), - (&[ 1, 1, 1], &[-1, -1], &[ 0, 1, 2]), - (&[ 2, 2, 1], &[-1, -2], &[ 1, 1, 2]) - ]; - - #[test] - fn test_add() { - for sum_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - - assert!(a + b == c); - assert!(b + a == c); - } - } - - #[test] - fn test_sub() { - for sum_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - - assert!(c - a == b); - assert!(c - b == a); - } - } - - static mul_triples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] = &[ - (&[], &[], &[]), - (&[], &[ 1], &[]), - (&[ 2], &[], &[]), - (&[ 1], &[ 1], &[1]), - (&[ 2], &[ 3], &[ 6]), - (&[ 1], &[ 1, 1, 1], &[1, 1, 1]), - (&[ 1, 2, 3], &[ 3], &[ 3, 6, 9]), - (&[ 1, 1, 1], &[-1], &[-1, -1, -1]), - (&[ 1, 2, 3], &[-1], &[-1, -2, -2, 2]), - (&[ 1, 2, 3, 4], &[-1], &[-1, -2, -2, -2, 3]), - (&[-1], &[-1], &[ 1, -2]), - (&[-1, -1], &[-1], &[ 1, -1, -2]), - (&[-1, -1, -1], &[-1], &[ 1, -1, -1, -2]), - (&[-1, -1, -1, -1], &[-1], &[ 1, -1, -1, -1, -2]), - (&[-1/2 + 1], &[ 2], &[ 0, 1]), - (&[0, -1/2 + 1], &[ 2], &[ 0, 0, 1]), - (&[ 1, 2], &[ 1, 2, 3], &[1, 4, 7, 6]), - (&[-1, -1], &[-1, -1, -1], &[1, 0, -1, -2, -1]), - (&[-1, -1, -1], &[-1, -1, -1, -1], &[1, 0, 0, -1, -2, -1, -1]), - (&[ 0, 0, 1], &[ 1, 2, 3], &[0, 0, 1, 2, 3]), - (&[ 0, 0, 1], &[ 0, 0, 0, 1], &[0, 0, 0, 0, 0, 1]) - ]; - - static div_rem_quadruples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] - = &[ - (&[ 1], &[ 2], &[], &[1]), - (&[ 1, 1], &[ 2], &[-1/2+1], &[1]), - (&[ 1, 1, 1], &[ 2], &[-1/2+1, -1/2+1], &[1]), - (&[ 0, 1], &[-1], &[1], &[1]), - (&[-1, -1], &[-2], &[2, 1], &[3]) - ]; - - #[test] - fn test_mul() { - for mul_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - - assert!(a * b == c); - assert!(b * a == c); - } - - for div_rem_quadruples.each |elm| { - let (aVec, bVec, cVec, dVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - let d = BigUint::from_slice(dVec); - - assert!(a == b * c + d); - assert!(a == c * b + d); - } - } - - #[test] - fn test_div_rem() { - for mul_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - - if !a.is_zero() { - assert_eq!(c.div_rem(&a), (b.clone(), Zero::zero())); - } - if !b.is_zero() { - assert_eq!(c.div_rem(&b), (a.clone(), Zero::zero())); - } - } - - for div_rem_quadruples.each |elm| { - let (aVec, bVec, cVec, dVec) = *elm; - let a = BigUint::from_slice(aVec); - let b = BigUint::from_slice(bVec); - let c = BigUint::from_slice(cVec); - let d = BigUint::from_slice(dVec); - - if !b.is_zero() { assert!(a.div_rem(&b) == (c, d)); } - } - } - - #[test] - fn test_gcd() { - fn check(a: uint, b: uint, c: uint) { - let big_a = BigUint::from_uint(a); - let big_b = BigUint::from_uint(b); - let big_c = BigUint::from_uint(c); - - assert_eq!(big_a.gcd(&big_b), big_c); - } - - check(10, 2, 2); - check(10, 3, 1); - check(0, 3, 3); - check(3, 3, 3); - check(56, 42, 14); - } - - #[test] - fn test_lcm() { - fn check(a: uint, b: uint, c: uint) { - let big_a = BigUint::from_uint(a); - let big_b = BigUint::from_uint(b); - let big_c = BigUint::from_uint(c); - - assert_eq!(big_a.lcm(&big_b), big_c); - } - - check(1, 0, 0); - check(0, 1, 0); - check(1, 1, 1); - check(8, 9, 72); - check(11, 5, 55); - check(99, 17, 1683); - } - - #[test] - fn test_is_even() { - assert!(FromStr::from_str::<BigUint>("1").get().is_odd()); - assert!(FromStr::from_str::<BigUint>("2").get().is_even()); - assert!(FromStr::from_str::<BigUint>("1000").get().is_even()); - assert!(FromStr::from_str::<BigUint>("1000000000000000000000").get().is_even()); - assert!(FromStr::from_str::<BigUint>("1000000000000000000001").get().is_odd()); - assert!((BigUint::from_uint(1) << 64).is_even()); - assert!(((BigUint::from_uint(1) << 64) + BigUint::from_uint(1)).is_odd()); - } - - fn to_str_pairs() -> ~[ (BigUint, ~[(uint, ~str)]) ] { - let bits = BigDigit::bits; - ~[( Zero::zero(), ~[ - (2, ~"0"), (3, ~"0") - ]), ( BigUint::from_slice([ 0xff ]), ~[ - (2, ~"11111111"), - (3, ~"100110"), - (4, ~"3333"), - (5, ~"2010"), - (6, ~"1103"), - (7, ~"513"), - (8, ~"377"), - (9, ~"313"), - (10, ~"255"), - (11, ~"212"), - (12, ~"193"), - (13, ~"168"), - (14, ~"143"), - (15, ~"120"), - (16, ~"ff") - ]), ( BigUint::from_slice([ 0xfff ]), ~[ - (2, ~"111111111111"), - (4, ~"333333"), - (16, ~"fff") - ]), ( BigUint::from_slice([ 1, 2 ]), ~[ - (2, - ~"10" + - str::from_chars(vec::from_elem(bits - 1, '0')) + "1"), - (4, - ~"2" + - str::from_chars(vec::from_elem(bits / 2 - 1, '0')) + "1"), - (10, match bits { - 32 => ~"8589934593", 16 => ~"131073", _ => fail!() - }), - (16, - ~"2" + - str::from_chars(vec::from_elem(bits / 4 - 1, '0')) + "1") - ]), ( BigUint::from_slice([ 1, 2, 3 ]), ~[ - (2, - ~"11" + - str::from_chars(vec::from_elem(bits - 2, '0')) + "10" + - str::from_chars(vec::from_elem(bits - 1, '0')) + "1"), - (4, - ~"3" + - str::from_chars(vec::from_elem(bits / 2 - 1, '0')) + "2" + - str::from_chars(vec::from_elem(bits / 2 - 1, '0')) + "1"), - (10, match bits { - 32 => ~"55340232229718589441", - 16 => ~"12885032961", - _ => fail!() - }), - (16, ~"3" + - str::from_chars(vec::from_elem(bits / 4 - 1, '0')) + "2" + - str::from_chars(vec::from_elem(bits / 4 - 1, '0')) + "1") - ]) ] - } - - #[test] - fn test_to_str_radix() { - for to_str_pairs().each |num_pair| { - let &(n, rs) = num_pair; - for rs.each |str_pair| { - let &(radix, str) = str_pair; - assert_eq!(n.to_str_radix(radix), str); - } - } - } - - #[test] - fn test_from_str_radix() { - for to_str_pairs().each |num_pair| { - let &(n, rs) = num_pair; - for rs.each |str_pair| { - let &(radix, str) = str_pair; - assert_eq!(&n, &FromStrRadix::from_str_radix(str, radix).get()); - } - } - - assert_eq!(FromStrRadix::from_str_radix::<BigUint>(~"Z", 10), None); - assert_eq!(FromStrRadix::from_str_radix::<BigUint>(~"_", 2), None); - assert_eq!(FromStrRadix::from_str_radix::<BigUint>(~"-1", 10), None); - } - - #[test] - fn test_factor() { - fn factor(n: uint) -> BigUint { - let mut f= One::one::<BigUint>(); - for uint::range(2, n + 1) |i| { - // FIXME(#6102): Assignment operator for BigInt causes ICE - // f *= BigUint::from_uint(i); - f = f * BigUint::from_uint(i); - } - return f; - } - - fn check(n: uint, s: &str) { - let n = factor(n); - let ans = match FromStrRadix::from_str_radix(s, 10) { - Some(x) => x, None => fail!() - }; - assert_eq!(n, ans); - } - - check(3, "6"); - check(10, "3628800"); - check(20, "2432902008176640000"); - check(30, "265252859812191058636308480000000"); - } -} - -#[cfg(test)] -mod bigint_tests { - use super::*; - use core::cmp::{Less, Equal, Greater}; - use core::num::{IntConvertible, Zero, One, FromStrRadix}; - - #[test] - fn test_from_biguint() { - fn check(inp_s: Sign, inp_n: uint, ans_s: Sign, ans_n: uint) { - let inp = BigInt::from_biguint(inp_s, BigUint::from_uint(inp_n)); - let ans = BigInt { sign: ans_s, data: BigUint::from_uint(ans_n)}; - assert_eq!(inp, ans); - } - check(Plus, 1, Plus, 1); - check(Plus, 0, Zero, 0); - check(Minus, 1, Minus, 1); - check(Zero, 1, Zero, 0); - } - - #[test] - fn test_cmp() { - let vs = [ &[2], &[1, 1], &[2, 1], &[1, 1, 1] ]; - let mut nums = vec::reversed(vs) - .map(|s| BigInt::from_slice(Minus, *s)); - nums.push(Zero::zero()); - nums.push_all_move(vs.map(|s| BigInt::from_slice(Plus, *s))); - - for nums.eachi |i, ni| { - for vec::slice(nums, i, nums.len()).eachi |j0, nj| { - let j = i + j0; - if i == j { - assert_eq!(ni.cmp(nj), Equal); - assert_eq!(nj.cmp(ni), Equal); - assert_eq!(ni, nj); - assert!(!(ni != nj)); - assert!(ni <= nj); - assert!(ni >= nj); - assert!(!(ni < nj)); - assert!(!(ni > nj)); - } else { - assert_eq!(ni.cmp(nj), Less); - assert_eq!(nj.cmp(ni), Greater); - - assert!(!(ni == nj)); - assert!(ni != nj); - - assert!(ni <= nj); - assert!(!(ni >= nj)); - assert!(ni < nj); - assert!(!(ni > nj)); - - assert!(!(nj <= ni)); - assert!(nj >= ni); - assert!(!(nj < ni)); - assert!(nj > ni); - } - } - } - } - - #[test] - fn test_convert_int() { - fn check(b: BigInt, i: int) { - assert!(b == IntConvertible::from_int(i)); - assert!(b.to_int() == i); - } - - check(Zero::zero(), 0); - check(One::one(), 1); - check(BigInt::from_biguint( - Plus, BigUint::from_uint(int::max_value as uint) - ), int::max_value); - - assert!(BigInt::from_biguint( - Plus, BigUint::from_uint(int::max_value as uint + 1) - ).to_int() == int::max_value); - assert!(BigInt::from_biguint( - Plus, BigUint::new(~[1, 2, 3]) - ).to_int() == int::max_value); - - check(BigInt::from_biguint( - Minus, BigUint::from_uint(-int::min_value as uint) - ), int::min_value); - assert!(BigInt::from_biguint( - Minus, BigUint::from_uint(-int::min_value as uint + 1) - ).to_int() == int::min_value); - assert!(BigInt::from_biguint( - Minus, BigUint::new(~[1, 2, 3]) - ).to_int() == int::min_value); - } - - #[test] - fn test_convert_uint() { - fn check(b: BigInt, u: uint) { - assert!(b == BigInt::from_uint(u)); - assert!(b.to_uint() == u); - } - - check(Zero::zero(), 0); - check(One::one(), 1); - - check( - BigInt::from_biguint(Plus, BigUint::from_uint(uint::max_value)), - uint::max_value); - assert!(BigInt::from_biguint( - Plus, BigUint::new(~[1, 2, 3]) - ).to_uint() == uint::max_value); - - assert!(BigInt::from_biguint( - Minus, BigUint::from_uint(uint::max_value) - ).to_uint() == 0); - assert!(BigInt::from_biguint( - Minus, BigUint::new(~[1, 2, 3]) - ).to_uint() == 0); - } - - static sum_triples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] = &[ - (&[], &[], &[]), - (&[], &[ 1], &[ 1]), - (&[ 1], &[ 1], &[ 2]), - (&[ 1], &[ 1, 1], &[ 2, 1]), - (&[ 1], &[-1], &[ 0, 1]), - (&[ 1], &[-1, -1], &[ 0, 0, 1]), - (&[-1, -1], &[-1, -1], &[-2, -1, 1]), - (&[ 1, 1, 1], &[-1, -1], &[ 0, 1, 2]), - (&[ 2, 2, 1], &[-1, -2], &[ 1, 1, 2]) - ]; - - #[test] - fn test_add() { - for sum_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - - assert!(a + b == c); - assert!(b + a == c); - assert!(c + (-a) == b); - assert!(c + (-b) == a); - assert!(a + (-c) == (-b)); - assert!(b + (-c) == (-a)); - assert!((-a) + (-b) == (-c)) - assert!(a + (-a) == Zero::zero()); - } - } - - #[test] - fn test_sub() { - for sum_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - - assert!(c - a == b); - assert!(c - b == a); - assert!((-b) - a == (-c)) - assert!((-a) - b == (-c)) - assert!(b - (-a) == c); - assert!(a - (-b) == c); - assert!((-c) - (-a) == (-b)); - assert!(a - a == Zero::zero()); - } - } - - static mul_triples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] = &[ - (&[], &[], &[]), - (&[], &[ 1], &[]), - (&[ 2], &[], &[]), - (&[ 1], &[ 1], &[1]), - (&[ 2], &[ 3], &[ 6]), - (&[ 1], &[ 1, 1, 1], &[1, 1, 1]), - (&[ 1, 2, 3], &[ 3], &[ 3, 6, 9]), - (&[ 1, 1, 1], &[-1], &[-1, -1, -1]), - (&[ 1, 2, 3], &[-1], &[-1, -2, -2, 2]), - (&[ 1, 2, 3, 4], &[-1], &[-1, -2, -2, -2, 3]), - (&[-1], &[-1], &[ 1, -2]), - (&[-1, -1], &[-1], &[ 1, -1, -2]), - (&[-1, -1, -1], &[-1], &[ 1, -1, -1, -2]), - (&[-1, -1, -1, -1], &[-1], &[ 1, -1, -1, -1, -2]), - (&[-1/2 + 1], &[ 2], &[ 0, 1]), - (&[0, -1/2 + 1], &[ 2], &[ 0, 0, 1]), - (&[ 1, 2], &[ 1, 2, 3], &[1, 4, 7, 6]), - (&[-1, -1], &[-1, -1, -1], &[1, 0, -1, -2, -1]), - (&[-1, -1, -1], &[-1, -1, -1, -1], &[1, 0, 0, -1, -2, -1, -1]), - (&[ 0, 0, 1], &[ 1, 2, 3], &[0, 0, 1, 2, 3]), - (&[ 0, 0, 1], &[ 0, 0, 0, 1], &[0, 0, 0, 0, 0, 1]) - ]; - - static div_rem_quadruples: &'static [(&'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit], - &'static [BigDigit])] - = &[ - (&[ 1], &[ 2], &[], &[1]), - (&[ 1, 1], &[ 2], &[-1/2+1], &[1]), - (&[ 1, 1, 1], &[ 2], &[-1/2+1, -1/2+1], &[1]), - (&[ 0, 1], &[-1], &[1], &[1]), - (&[-1, -1], &[-2], &[2, 1], &[3]) - ]; - - #[test] - fn test_mul() { - for mul_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - - assert!(a * b == c); - assert!(b * a == c); - - assert!((-a) * b == -c); - assert!((-b) * a == -c); - } - - for div_rem_quadruples.each |elm| { - let (aVec, bVec, cVec, dVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - let d = BigInt::from_slice(Plus, dVec); - - assert!(a == b * c + d); - assert!(a == c * b + d); - } - } - - #[test] - fn test_div_mod_floor() { - fn check_sub(a: &BigInt, b: &BigInt, ans_d: &BigInt, ans_m: &BigInt) { - let (d, m) = a.div_mod_floor(b); - if !m.is_zero() { - assert_eq!(m.sign, b.sign); - } - assert!(m.abs() <= b.abs()); - assert!(*a == b * d + m); - assert!(d == *ans_d); - assert!(m == *ans_m); - } - - fn check(a: &BigInt, b: &BigInt, d: &BigInt, m: &BigInt) { - if m.is_zero() { - check_sub(a, b, d, m); - check_sub(a, &b.neg(), &d.neg(), m); - check_sub(&a.neg(), b, &d.neg(), m); - check_sub(&a.neg(), &b.neg(), d, m); - } else { - check_sub(a, b, d, m); - check_sub(a, &b.neg(), &(d.neg() - One::one()), &(m - *b)); - check_sub(&a.neg(), b, &(d.neg() - One::one()), &(b - *m)); - check_sub(&a.neg(), &b.neg(), d, &m.neg()); - } - } - - for mul_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - - if !a.is_zero() { check(&c, &a, &b, &Zero::zero()); } - if !b.is_zero() { check(&c, &b, &a, &Zero::zero()); } - } - - for div_rem_quadruples.each |elm| { - let (aVec, bVec, cVec, dVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - let d = BigInt::from_slice(Plus, dVec); - - if !b.is_zero() { - check(&a, &b, &c, &d); - } - } - } - - - #[test] - fn test_div_rem() { - fn check_sub(a: &BigInt, b: &BigInt, ans_q: &BigInt, ans_r: &BigInt) { - let (q, r) = a.div_rem(b); - if !r.is_zero() { - assert_eq!(r.sign, a.sign); - } - assert!(r.abs() <= b.abs()); - assert!(*a == b * q + r); - assert!(q == *ans_q); - assert!(r == *ans_r); - } - - fn check(a: &BigInt, b: &BigInt, q: &BigInt, r: &BigInt) { - check_sub(a, b, q, r); - check_sub(a, &b.neg(), &q.neg(), r); - check_sub(&a.neg(), b, &q.neg(), &r.neg()); - check_sub(&a.neg(), &b.neg(), q, &r.neg()); - } - for mul_triples.each |elm| { - let (aVec, bVec, cVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - - if !a.is_zero() { check(&c, &a, &b, &Zero::zero()); } - if !b.is_zero() { check(&c, &b, &a, &Zero::zero()); } - } - - for div_rem_quadruples.each |elm| { - let (aVec, bVec, cVec, dVec) = *elm; - let a = BigInt::from_slice(Plus, aVec); - let b = BigInt::from_slice(Plus, bVec); - let c = BigInt::from_slice(Plus, cVec); - let d = BigInt::from_slice(Plus, dVec); - - if !b.is_zero() { - check(&a, &b, &c, &d); - } - } - } - - #[test] - fn test_gcd() { - fn check(a: int, b: int, c: int) { - let big_a: BigInt = IntConvertible::from_int(a); - let big_b: BigInt = IntConvertible::from_int(b); - let big_c: BigInt = IntConvertible::from_int(c); - - assert_eq!(big_a.gcd(&big_b), big_c); - } - - check(10, 2, 2); - check(10, 3, 1); - check(0, 3, 3); - check(3, 3, 3); - check(56, 42, 14); - check(3, -3, 3); - check(-6, 3, 3); - check(-4, -2, 2); - } - - #[test] - fn test_lcm() { - fn check(a: int, b: int, c: int) { - let big_a: BigInt = IntConvertible::from_int(a); - let big_b: BigInt = IntConvertible::from_int(b); - let big_c: BigInt = IntConvertible::from_int(c); - - assert_eq!(big_a.lcm(&big_b), big_c); - } - - check(1, 0, 0); - check(0, 1, 0); - check(1, 1, 1); - check(-1, 1, 1); - check(1, -1, 1); - check(-1, -1, 1); - check(8, 9, 72); - check(11, 5, 55); - } - - #[test] - fn test_abs_sub() { - assert_eq!((-One::one::<BigInt>()).abs_sub(&One::one()), Zero::zero()); - assert_eq!(One::one::<BigInt>().abs_sub(&One::one()), Zero::zero()); - assert_eq!(One::one::<BigInt>().abs_sub(&Zero::zero()), One::one()); - assert_eq!(One::one::<BigInt>().abs_sub(&-One::one::<BigInt>()), - IntConvertible::from_int(2)); - } - - #[test] - fn test_to_str_radix() { - fn check(n: int, ans: &str) { - assert!(ans == IntConvertible::from_int::<BigInt>(n).to_str_radix(10)); - } - check(10, "10"); - check(1, "1"); - check(0, "0"); - check(-1, "-1"); - check(-10, "-10"); - } - - - #[test] - fn test_from_str_radix() { - fn check(s: &str, ans: Option<int>) { - let ans = ans.map(|&n| IntConvertible::from_int::<BigInt>(n)); - assert_eq!(FromStrRadix::from_str_radix(s, 10), ans); - } - check("10", Some(10)); - check("1", Some(1)); - check("0", Some(0)); - check("-1", Some(-1)); - check("-10", Some(-10)); - check("Z", None); - check("_", None); - } - - #[test] - fn test_neg() { - assert!(-BigInt::new(Plus, ~[1, 1, 1]) == - BigInt::new(Minus, ~[1, 1, 1])); - assert!(-BigInt::new(Minus, ~[1, 1, 1]) == - BigInt::new(Plus, ~[1, 1, 1])); - assert_eq!(-Zero::zero::<BigInt>(), Zero::zero::<BigInt>()); - } -} diff --git a/src/libstd/num/cmath.rs b/src/libstd/num/cmath.rs new file mode 100644 index 00000000000..a80703fafa3 --- /dev/null +++ b/src/libstd/num/cmath.rs @@ -0,0 +1,270 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; // FIXME #3538 + +// function names are almost identical to C's libmath, a few have been +// renamed, grep for "rename:" + +pub mod c_double_utils { + use libc::{c_double, c_int}; + + #[link_name = "m"] + #[abi = "cdecl"] + pub extern { + // Alpabetically sorted by link_name + + unsafe fn acos(n: c_double) -> c_double; + unsafe fn asin(n: c_double) -> c_double; + unsafe fn atan(n: c_double) -> c_double; + unsafe fn atan2(a: c_double, b: c_double) -> c_double; + unsafe fn cbrt(n: c_double) -> c_double; + unsafe fn ceil(n: c_double) -> c_double; + unsafe fn copysign(x: c_double, y: c_double) -> c_double; + unsafe fn cos(n: c_double) -> c_double; + unsafe fn cosh(n: c_double) -> c_double; + unsafe fn erf(n: c_double) -> c_double; + unsafe fn erfc(n: c_double) -> c_double; + unsafe fn exp(n: c_double) -> c_double; + // rename: for consistency with underscore usage elsewhere + #[link_name="expm1"] unsafe fn exp_m1(n: c_double) -> c_double; + unsafe fn exp2(n: c_double) -> c_double; + #[link_name="fabs"] unsafe fn abs(n: c_double) -> c_double; + // rename: for clarity and consistency with add/sub/mul/div + #[link_name="fdim"] + unsafe fn abs_sub(a: c_double, b: c_double) -> c_double; + unsafe fn floor(n: c_double) -> c_double; + // rename: for clarity and consistency with add/sub/mul/div + #[link_name="fma"] + unsafe fn mul_add(a: c_double, b: c_double, c: c_double) -> c_double; + #[link_name="fmax"] + unsafe fn fmax(a: c_double, b: c_double) -> c_double; + #[link_name="fmin"] + unsafe fn fmin(a: c_double, b: c_double) -> c_double; + #[link_name="nextafter"] + unsafe fn next_after(x: c_double, y: c_double) -> c_double; + unsafe fn frexp(n: c_double, value: &mut c_int) -> c_double; + unsafe fn hypot(x: c_double, y: c_double) -> c_double; + unsafe fn ldexp(x: c_double, n: c_int) -> c_double; + #[cfg(unix)] + #[link_name="lgamma_r"] + unsafe fn lgamma(n: c_double, sign: &mut c_int) -> c_double; + #[cfg(windows)] + #[link_name="__lgamma_r"] + unsafe fn lgamma(n: c_double, sign: &mut c_int) -> c_double; + // renamed: log is a reserved keyword; ln seems more natural, too + #[link_name="log"] unsafe fn ln(n: c_double) -> c_double; + // renamed: "logb" /often/ is confused for log2 by beginners + #[link_name="logb"] unsafe fn log_radix(n: c_double) -> c_double; + // renamed: to be consitent with log as ln + #[link_name="log1p"] unsafe fn ln_1p(n: c_double) -> c_double; + unsafe fn log10(n: c_double) -> c_double; + unsafe fn log2(n: c_double) -> c_double; + #[link_name="ilogb"] unsafe fn ilog_radix(n: c_double) -> c_int; + unsafe fn modf(n: c_double, iptr: &mut c_double) -> c_double; + unsafe fn pow(n: c_double, e: c_double) -> c_double; + // FIXME (#1379): enable when rounding modes become available + // unsafe fn rint(n: c_double) -> c_double; + unsafe fn round(n: c_double) -> c_double; + // rename: for consistency with logradix + #[link_name="scalbn"] unsafe fn ldexp_radix(n: c_double, i: c_int) -> + c_double; + unsafe fn sin(n: c_double) -> c_double; + unsafe fn sinh(n: c_double) -> c_double; + unsafe fn sqrt(n: c_double) -> c_double; + unsafe fn tan(n: c_double) -> c_double; + unsafe fn tanh(n: c_double) -> c_double; + unsafe fn tgamma(n: c_double) -> c_double; + unsafe fn trunc(n: c_double) -> c_double; + + // These are commonly only available for doubles + + unsafe fn j0(n: c_double) -> c_double; + unsafe fn j1(n: c_double) -> c_double; + unsafe fn jn(i: c_int, n: c_double) -> c_double; + + unsafe fn y0(n: c_double) -> c_double; + unsafe fn y1(n: c_double) -> c_double; + unsafe fn yn(i: c_int, n: c_double) -> c_double; + } +} + +pub mod c_float_utils { + use libc::{c_float, c_int}; + + #[link_name = "m"] + #[abi = "cdecl"] + pub extern { + // Alpabetically sorted by link_name + + #[link_name="acosf"] unsafe fn acos(n: c_float) -> c_float; + #[link_name="asinf"] unsafe fn asin(n: c_float) -> c_float; + #[link_name="atanf"] unsafe fn atan(n: c_float) -> c_float; + #[link_name="atan2f"] + unsafe fn atan2(a: c_float, b: c_float) -> c_float; + #[link_name="cbrtf"] unsafe fn cbrt(n: c_float) -> c_float; + #[link_name="ceilf"] unsafe fn ceil(n: c_float) -> c_float; + #[link_name="copysignf"] unsafe fn copysign(x: c_float, + y: c_float) -> c_float; + #[link_name="cosf"] unsafe fn cos(n: c_float) -> c_float; + #[link_name="coshf"] unsafe fn cosh(n: c_float) -> c_float; + #[link_name="erff"] unsafe fn erf(n: c_float) -> c_float; + #[link_name="erfcf"] unsafe fn erfc(n: c_float) -> c_float; + #[link_name="expf"] unsafe fn exp(n: c_float) -> c_float; + #[link_name="expm1f"]unsafe fn exp_m1(n: c_float) -> c_float; + #[link_name="exp2f"] unsafe fn exp2(n: c_float) -> c_float; + #[link_name="fabsf"] unsafe fn abs(n: c_float) -> c_float; + #[link_name="fdimf"] + unsafe fn abs_sub(a: c_float, b: c_float) -> c_float; + #[link_name="floorf"] unsafe fn floor(n: c_float) -> c_float; + #[link_name="frexpf"] unsafe fn frexp(n: c_float, + value: &mut c_int) -> c_float; + #[link_name="fmaf"] + unsafe fn mul_add(a: c_float, b: c_float, c: c_float) -> c_float; + #[link_name="fmaxf"] + unsafe fn fmax(a: c_float, b: c_float) -> c_float; + #[link_name="fminf"] + unsafe fn fmin(a: c_float, b: c_float) -> c_float; + #[link_name="nextafterf"] + unsafe fn next_after(x: c_float, y: c_float) -> c_float; + #[link_name="hypotf"] + unsafe fn hypot(x: c_float, y: c_float) -> c_float; + #[link_name="ldexpf"] + unsafe fn ldexp(x: c_float, n: c_int) -> c_float; + + #[cfg(unix)] + #[link_name="lgammaf_r"] unsafe fn lgamma(n: c_float, + sign: &mut c_int) -> c_float; + + #[cfg(windows)] + #[link_name="__lgammaf_r"] + unsafe fn lgamma(n: c_float, sign: &mut c_int) -> c_float; + + #[link_name="logf"] unsafe fn ln(n: c_float) -> c_float; + #[link_name="logbf"] unsafe fn log_radix(n: c_float) -> c_float; + #[link_name="log1pf"] unsafe fn ln_1p(n: c_float) -> c_float; + #[link_name="log2f"] unsafe fn log2(n: c_float) -> c_float; + #[link_name="log10f"] unsafe fn log10(n: c_float) -> c_float; + #[link_name="ilogbf"] unsafe fn ilog_radix(n: c_float) -> c_int; + #[link_name="modff"] unsafe fn modf(n: c_float, + iptr: &mut c_float) -> c_float; + #[link_name="powf"] unsafe fn pow(n: c_float, e: c_float) -> c_float; + // FIXME (#1379): enable when rounding modes become available + // #[link_name="rintf"] unsafe fn rint(n: c_float) -> c_float; + #[link_name="roundf"] unsafe fn round(n: c_float) -> c_float; + #[link_name="scalbnf"] unsafe fn ldexp_radix(n: c_float, i: c_int) + -> c_float; + #[link_name="sinf"] unsafe fn sin(n: c_float) -> c_float; + #[link_name="sinhf"] unsafe fn sinh(n: c_float) -> c_float; + #[link_name="sqrtf"] unsafe fn sqrt(n: c_float) -> c_float; + #[link_name="tanf"] unsafe fn tan(n: c_float) -> c_float; + #[link_name="tanhf"] unsafe fn tanh(n: c_float) -> c_float; + #[link_name="tgammaf"] unsafe fn tgamma(n: c_float) -> c_float; + #[link_name="truncf"] unsafe fn trunc(n: c_float) -> c_float; + } +} + +// PORT check these by running src/etc/machconsts.c for your architecture + +// FIXME obtain machine float/math constants automatically (Issue #1986) + +pub mod c_float_targ_consts { + pub static radix: uint = 2u; + pub static mantissa_digits: uint = 24u; + pub static digits: uint = 6u; + pub static min_exp: uint = -125u; + pub static max_exp: uint = 128u; + pub static min_10_exp: int = -37; + pub static max_10_exp: int = 38; + // FIXME (#1433): this is wrong, replace with hexadecimal (%a) staticants + // below. + pub static min_value: f32 = 1.175494e-38_f32; + pub static max_value: f32 = 3.402823e+38_f32; + pub static epsilon: f32 = 0.000000_f32; +} + +pub mod c_double_targ_consts { + pub static radix: uint = 2u; + pub static mantissa_digits: uint = 53u; + pub static digits: uint = 15u; + pub static min_exp: uint = -1021u; + pub static max_exp: uint = 1024u; + pub static min_10_exp: int = -307; + pub static max_10_exp: int = 308; + // FIXME (#1433): this is wrong, replace with hexadecimal (%a) staticants + // below. + pub static min_value: f64 = 2.225074e-308_f64; + pub static max_value: f64 = 1.797693e+308_f64; + pub static epsilon: f64 = 2.220446e-16_f64; +} + +/* + +FIXME use these once they can be parsed (see Issue #1433) + +pub mod c_float_math_consts { + pub static pi: c_float = 0x1.921fb6p+1_f32; + pub static div_1_pi: c_float = 0x1.45f306p-2_f32; + pub static div_2_pi: c_float = 0x1.45f306p-1_f32; + pub static div_pi_2: c_float = 0x1.921fb6p+0_f32; + pub static div_pi_4: c_float = 0x1.921fb6p-1_f32; + pub static div_2_sqrtpi: c_float = 0x1.20dd76p+0_f32; + pub static e: c_float = 0x1.5bf0a8p+1_f32; + pub static log2_e: c_float = 0x1.715476p+0_f32; + pub static log10_e: c_float = 0x1.bcb7b2p-2_f32; + pub static ln_2: c_float = 0x1.62e43p-1_f32; + pub static ln_10: c_float = 0x1.26bb1cp+1_f32; + pub static sqrt2: c_float = 0x1.6a09e6p+0_f32; + pub static div_1_sqrt2: c_float = 0x1.6a09e6p-1_f32; +} + +pub mod c_double_math_consts { + pub static pi: c_double = 0x1.921fb54442d18p+1_f64; + pub static div_1_pi: c_double = 0x1.45f306dc9c883p-2_f64; + pub static div_2_pi: c_double = 0x1.45f306dc9c883p-1_f64; + pub static div_pi_2: c_double = 0x1.921fb54442d18p+0_f64; + pub static div_pi_4: c_double = 0x1.921fb54442d18p-1_f64; + pub static div_2_sqrtpi: c_double = 0x1.20dd750429b6dp+0_f64; + pub static e: c_double = 0x1.5bf0a8b145769p+1_f64; + pub static log2_e: c_double = 0x1.71547652b82fep+0_f64; + pub static log10_e: c_double = 0x1.bcb7b1526e50ep-2_f64; + pub static ln_2: c_double = 0x1.62e42fefa39efp-1_f64; + pub static ln_10: c_double = 0x1.26bb1bbb55516p+1_f64; + pub static sqrt2: c_double = 0x1.6a09e667f3bcdp+0_f64; + pub static div_1_sqrt2: c_double = 0x1.6a09e667f3bcdp-1_f64; +} + +pub mod c_float_targ_consts { + pub static radix: uint = 2u; + pub static mantissa_digits: uint = 24u; + pub static digits: uint = 6u; + pub static min_exp: int = -125; + pub static max_exp: int = 128; + pub static min_10_exp: int = -37; + pub static max_10_exp: int = 38; + pub static min_value: c_float = 0x1p-126_f32; + pub static max_value: c_float = 0x1.fffffep+127_f32; + pub static epsilon: c_float = 0x1p-23_f32; +} + +pub mod c_double_targ_consts { + pub static radix: uint = 2u; + pub static mantissa_digits: uint = 53u; + pub static digits: uint = 15u; + pub static min_exp: int = -1021; + pub static max_exp: int = 1024; + pub static min_10_exp: int = -307; + pub static max_10_exp: int = 308; + pub static min_value: c_double = 0x1p-1022_f64; + pub static max_value: c_double = 0x1.fffffffffffffp+1023_f64; + pub static epsilon: c_double = 0x1p-52_f64; +} + +*/ diff --git a/src/libstd/num/complex.rs b/src/libstd/num/complex.rs deleted file mode 100644 index 41d2b4a101c..00000000000 --- a/src/libstd/num/complex.rs +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - - -//! Complex numbers. - -use core::num::{Zero,One,ToStrRadix}; - -// FIXME #1284: handle complex NaN & infinity etc. This -// probably doesn't map to C's _Complex correctly. - -// FIXME #5734:: Need generic sin/cos for .to/from_polar(). -// FIXME #5735: Need generic sqrt to implement .norm(). - - -/// A complex number in Cartesian form. -#[deriving(Eq,Clone)] -pub struct Cmplx<T> { - re: T, - im: T -} - -pub type Complex = Cmplx<float>; -pub type Complex32 = Cmplx<f32>; -pub type Complex64 = Cmplx<f64>; - -impl<T: Copy + Num> Cmplx<T> { - /// Create a new Cmplx - #[inline] - pub fn new(re: T, im: T) -> Cmplx<T> { - Cmplx { re: re, im: im } - } - - /** - Returns the square of the norm (since `T` doesn't necessarily - have a sqrt function), i.e. `re^2 + im^2`. - */ - #[inline] - pub fn norm_sqr(&self) -> T { - self.re * self.re + self.im * self.im - } - - - /// Returns the complex conjugate. i.e. `re - i im` - #[inline] - pub fn conj(&self) -> Cmplx<T> { - Cmplx::new(self.re, -self.im) - } - - - /// Multiplies `self` by the scalar `t`. - #[inline] - pub fn scale(&self, t: T) -> Cmplx<T> { - Cmplx::new(self.re * t, self.im * t) - } - - /// Divides `self` by the scalar `t`. - #[inline] - pub fn unscale(&self, t: T) -> Cmplx<T> { - Cmplx::new(self.re / t, self.im / t) - } - - /// Returns `1/self` - #[inline] - pub fn inv(&self) -> Cmplx<T> { - let norm_sqr = self.norm_sqr(); - Cmplx::new(self.re / norm_sqr, - -self.im / norm_sqr) - } -} - -/* arithmetic */ -// (a + i b) + (c + i d) == (a + c) + i (b + d) -impl<T: Copy + Num> Add<Cmplx<T>, Cmplx<T>> for Cmplx<T> { - #[inline] - fn add(&self, other: &Cmplx<T>) -> Cmplx<T> { - Cmplx::new(self.re + other.re, self.im + other.im) - } -} -// (a + i b) - (c + i d) == (a - c) + i (b - d) -impl<T: Copy + Num> Sub<Cmplx<T>, Cmplx<T>> for Cmplx<T> { - #[inline] - fn sub(&self, other: &Cmplx<T>) -> Cmplx<T> { - Cmplx::new(self.re - other.re, self.im - other.im) - } -} -// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c) -impl<T: Copy + Num> Mul<Cmplx<T>, Cmplx<T>> for Cmplx<T> { - #[inline] - fn mul(&self, other: &Cmplx<T>) -> Cmplx<T> { - Cmplx::new(self.re*other.re - self.im*other.im, - self.re*other.im + self.im*other.re) - } -} - -// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d) -// == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)] -impl<T: Copy + Num> Div<Cmplx<T>, Cmplx<T>> for Cmplx<T> { - #[inline] - fn div(&self, other: &Cmplx<T>) -> Cmplx<T> { - let norm_sqr = other.norm_sqr(); - Cmplx::new((self.re*other.re + self.im*other.im) / norm_sqr, - (self.im*other.re - self.re*other.im) / norm_sqr) - } -} - -impl<T: Copy + Num> Neg<Cmplx<T>> for Cmplx<T> { - #[inline] - fn neg(&self) -> Cmplx<T> { - Cmplx::new(-self.re, -self.im) - } -} - -/* constants */ -impl<T: Copy + Num> Zero for Cmplx<T> { - #[inline] - fn zero() -> Cmplx<T> { - Cmplx::new(Zero::zero(), Zero::zero()) - } - - #[inline] - fn is_zero(&self) -> bool { - *self == Zero::zero() - } -} - -impl<T: Copy + Num> One for Cmplx<T> { - #[inline] - fn one() -> Cmplx<T> { - Cmplx::new(One::one(), Zero::zero()) - } -} - -/* string conversions */ -impl<T: ToStr + Num + Ord> ToStr for Cmplx<T> { - fn to_str(&self) -> ~str { - if self.im < Zero::zero() { - fmt!("%s-%si", self.re.to_str(), (-self.im).to_str()) - } else { - fmt!("%s+%si", self.re.to_str(), self.im.to_str()) - } - } -} - -impl<T: ToStrRadix + Num + Ord> ToStrRadix for Cmplx<T> { - fn to_str_radix(&self, radix: uint) -> ~str { - if self.im < Zero::zero() { - fmt!("%s-%si", self.re.to_str_radix(radix), (-self.im).to_str_radix(radix)) - } else { - fmt!("%s+%si", self.re.to_str_radix(radix), self.im.to_str_radix(radix)) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use core::num::{Zero,One}; - - pub static _0_0i : Complex = Cmplx { re: 0f, im: 0f }; - pub static _1_0i : Complex = Cmplx { re: 1f, im: 0f }; - pub static _1_1i : Complex = Cmplx { re: 1f, im: 1f }; - pub static _0_1i : Complex = Cmplx { re: 0f, im: 1f }; - pub static _neg1_1i : Complex = Cmplx { re: -1f, im: 1f }; - pub static _05_05i : Complex = Cmplx { re: 0.5f, im: 0.5f }; - pub static all_consts : [Complex, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i]; - - #[test] - fn test_consts() { - // check our constants are what Cmplx::new creates - fn test(c : Complex, r : float, i: float) { - assert_eq!(c, Cmplx::new(r,i)); - } - test(_0_0i, 0f, 0f); - test(_1_0i, 1f, 0f); - test(_1_1i, 1f, 1f); - test(_neg1_1i, -1f, 1f); - test(_05_05i, 0.5f, 0.5f); - - assert_eq!(_0_0i, Zero::zero()); - assert_eq!(_1_0i, One::one()); - } - - #[test] - fn test_norm_sqr() { - fn test(c: Complex, ns: float) { - assert_eq!(c.norm_sqr(), ns); - } - test(_0_0i, 0f); - test(_1_0i, 1f); - test(_1_1i, 2f); - test(_neg1_1i, 2f); - test(_05_05i, 0.5f); - } - - #[test] - fn test_scale_unscale() { - assert_eq!(_05_05i.scale(2f), _1_1i); - assert_eq!(_1_1i.unscale(2f), _05_05i); - for all_consts.each |&c| { - assert_eq!(c.scale(2f).unscale(2f), c); - } - } - - #[test] - fn test_conj() { - for all_consts.each |&c| { - assert_eq!(c.conj(), Cmplx::new(c.re, -c.im)); - assert_eq!(c.conj().conj(), c); - } - } - - #[test] - fn test_inv() { - assert_eq!(_1_1i.inv(), _05_05i.conj()); - assert_eq!(_1_0i.inv(), _1_0i.inv()); - } - - #[test] - #[should_fail] - #[ignore] - fn test_inv_zero() { - // FIXME #5736: should this really fail, or just NaN? - _0_0i.inv(); - } - - - mod arith { - use super::*; - use core::num::Zero; - - #[test] - fn test_add() { - assert_eq!(_05_05i + _05_05i, _1_1i); - assert_eq!(_0_1i + _1_0i, _1_1i); - assert_eq!(_1_0i + _neg1_1i, _0_1i); - - for all_consts.each |&c| { - assert_eq!(_0_0i + c, c); - assert_eq!(c + _0_0i, c); - } - } - - #[test] - fn test_sub() { - assert_eq!(_05_05i - _05_05i, _0_0i); - assert_eq!(_0_1i - _1_0i, _neg1_1i); - assert_eq!(_0_1i - _neg1_1i, _1_0i); - - for all_consts.each |&c| { - assert_eq!(c - _0_0i, c); - assert_eq!(c - c, _0_0i); - } - } - - #[test] - fn test_mul() { - assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2f)); - assert_eq!(_1_1i * _0_1i, _neg1_1i); - - // i^2 & i^4 - assert_eq!(_0_1i * _0_1i, -_1_0i); - assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i); - - for all_consts.each |&c| { - assert_eq!(c * _1_0i, c); - assert_eq!(_1_0i * c, c); - } - } - #[test] - fn test_div() { - assert_eq!(_neg1_1i / _0_1i, _1_1i); - for all_consts.each |&c| { - if c != Zero::zero() { - assert_eq!(c / c, _1_0i); - } - } - } - #[test] - fn test_neg() { - assert_eq!(-_1_0i + _0_1i, _neg1_1i); - assert_eq!((-_0_1i) * _0_1i, _1_0i); - for all_consts.each |&c| { - assert_eq!(-(-c), c); - } - } - } - - #[test] - fn test_to_str() { - fn test(c : Complex, s: ~str) { - assert_eq!(c.to_str(), s); - } - test(_0_0i, ~"0+0i"); - test(_1_0i, ~"1+0i"); - test(_0_1i, ~"0+1i"); - test(_1_1i, ~"1+1i"); - test(_neg1_1i, ~"-1+1i"); - test(-_neg1_1i, ~"1-1i"); - test(_05_05i, ~"0.5+0.5i"); - } -} diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs new file mode 100644 index 00000000000..d5dc0dd4730 --- /dev/null +++ b/src/libstd/num/f32.rs @@ -0,0 +1,1250 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `f32` + +use libc::c_int; +use num::{Zero, One, strconv}; +use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal}; +use prelude::*; + +pub use cmath::c_float_targ_consts::*; + +// An inner module is required to get the #[inline(always)] attribute on the +// functions. +pub use self::delegated::*; + +macro_rules! delegate( + ( + $( + fn $name:ident( + $( + $arg:ident : $arg_ty:ty + ),* + ) -> $rv:ty = $bound_name:path + ),* + ) => ( + mod delegated { + use cmath::c_float_utils; + use libc::{c_float, c_int}; + use unstable::intrinsics; + + $( + #[inline(always)] + pub fn $name($( $arg : $arg_ty ),*) -> $rv { + unsafe { + $bound_name($( $arg ),*) + } + } + )* + } + ) +) + +delegate!( + // intrinsics + fn abs(n: f32) -> f32 = intrinsics::fabsf32, + fn cos(n: f32) -> f32 = intrinsics::cosf32, + fn exp(n: f32) -> f32 = intrinsics::expf32, + fn exp2(n: f32) -> f32 = intrinsics::exp2f32, + fn floor(x: f32) -> f32 = intrinsics::floorf32, + fn ln(n: f32) -> f32 = intrinsics::logf32, + fn log10(n: f32) -> f32 = intrinsics::log10f32, + fn log2(n: f32) -> f32 = intrinsics::log2f32, + fn mul_add(a: f32, b: f32, c: f32) -> f32 = intrinsics::fmaf32, + fn pow(n: f32, e: f32) -> f32 = intrinsics::powf32, + fn powi(n: f32, e: c_int) -> f32 = intrinsics::powif32, + fn sin(n: f32) -> f32 = intrinsics::sinf32, + fn sqrt(n: f32) -> f32 = intrinsics::sqrtf32, + + // LLVM 3.3 required to use intrinsics for these four + fn ceil(n: c_float) -> c_float = c_float_utils::ceil, + fn trunc(n: c_float) -> c_float = c_float_utils::trunc, + /* + fn ceil(n: f32) -> f32 = intrinsics::ceilf32, + fn trunc(n: f32) -> f32 = intrinsics::truncf32, + fn rint(n: f32) -> f32 = intrinsics::rintf32, + fn nearbyint(n: f32) -> f32 = intrinsics::nearbyintf32, + */ + + // cmath + fn acos(n: c_float) -> c_float = c_float_utils::acos, + fn asin(n: c_float) -> c_float = c_float_utils::asin, + fn atan(n: c_float) -> c_float = c_float_utils::atan, + fn atan2(a: c_float, b: c_float) -> c_float = c_float_utils::atan2, + fn cbrt(n: c_float) -> c_float = c_float_utils::cbrt, + fn copysign(x: c_float, y: c_float) -> c_float = c_float_utils::copysign, + fn cosh(n: c_float) -> c_float = c_float_utils::cosh, + fn erf(n: c_float) -> c_float = c_float_utils::erf, + fn erfc(n: c_float) -> c_float = c_float_utils::erfc, + fn exp_m1(n: c_float) -> c_float = c_float_utils::exp_m1, + fn abs_sub(a: c_float, b: c_float) -> c_float = c_float_utils::abs_sub, + fn fmax(a: c_float, b: c_float) -> c_float = c_float_utils::fmax, + fn fmin(a: c_float, b: c_float) -> c_float = c_float_utils::fmin, + fn next_after(x: c_float, y: c_float) -> c_float = c_float_utils::next_after, + fn frexp(n: c_float, value: &mut c_int) -> c_float = c_float_utils::frexp, + fn hypot(x: c_float, y: c_float) -> c_float = c_float_utils::hypot, + fn ldexp(x: c_float, n: c_int) -> c_float = c_float_utils::ldexp, + fn lgamma(n: c_float, sign: &mut c_int) -> c_float = c_float_utils::lgamma, + fn log_radix(n: c_float) -> c_float = c_float_utils::log_radix, + fn ln_1p(n: c_float) -> c_float = c_float_utils::ln_1p, + fn ilog_radix(n: c_float) -> c_int = c_float_utils::ilog_radix, + fn modf(n: c_float, iptr: &mut c_float) -> c_float = c_float_utils::modf, + fn round(n: c_float) -> c_float = c_float_utils::round, + fn ldexp_radix(n: c_float, i: c_int) -> c_float = c_float_utils::ldexp_radix, + fn sinh(n: c_float) -> c_float = c_float_utils::sinh, + fn tan(n: c_float) -> c_float = c_float_utils::tan, + fn tanh(n: c_float) -> c_float = c_float_utils::tanh, + fn tgamma(n: c_float) -> c_float = c_float_utils::tgamma +) + +// These are not defined inside consts:: for consistency with +// the integer types + +pub static NaN: f32 = 0.0_f32/0.0_f32; + +pub static infinity: f32 = 1.0_f32/0.0_f32; + +pub static neg_infinity: f32 = -1.0_f32/0.0_f32; + +#[inline(always)] +pub fn add(x: f32, y: f32) -> f32 { return x + y; } + +#[inline(always)] +pub fn sub(x: f32, y: f32) -> f32 { return x - y; } + +#[inline(always)] +pub fn mul(x: f32, y: f32) -> f32 { return x * y; } + +#[inline(always)] +pub fn div(x: f32, y: f32) -> f32 { return x / y; } + +#[inline(always)] +pub fn rem(x: f32, y: f32) -> f32 { return x % y; } + +#[inline(always)] +pub fn lt(x: f32, y: f32) -> bool { return x < y; } + +#[inline(always)] +pub fn le(x: f32, y: f32) -> bool { return x <= y; } + +#[inline(always)] +pub fn eq(x: f32, y: f32) -> bool { return x == y; } + +#[inline(always)] +pub fn ne(x: f32, y: f32) -> bool { return x != y; } + +#[inline(always)] +pub fn ge(x: f32, y: f32) -> bool { return x >= y; } + +#[inline(always)] +pub fn gt(x: f32, y: f32) -> bool { return x > y; } + + +// FIXME (#1999): replace the predicates below with llvm intrinsics or +// calls to the libmath macros in the rust runtime for performance. + +// FIXME (#1999): add is_normal, is_subnormal, and fpclassify. + +/* Module: consts */ +pub mod consts { + // FIXME (requires Issue #1433 to fix): replace with mathematical + // staticants from cmath. + /// Archimedes' staticant + pub static pi: f32 = 3.14159265358979323846264338327950288_f32; + + /// pi/2.0 + pub static frac_pi_2: f32 = 1.57079632679489661923132169163975144_f32; + + /// pi/4.0 + pub static frac_pi_4: f32 = 0.785398163397448309615660845819875721_f32; + + /// 1.0/pi + pub static frac_1_pi: f32 = 0.318309886183790671537767526745028724_f32; + + /// 2.0/pi + pub static frac_2_pi: f32 = 0.636619772367581343075535053490057448_f32; + + /// 2.0/sqrt(pi) + pub static frac_2_sqrtpi: f32 = 1.12837916709551257389615890312154517_f32; + + /// sqrt(2.0) + pub static sqrt2: f32 = 1.41421356237309504880168872420969808_f32; + + /// 1.0/sqrt(2.0) + pub static frac_1_sqrt2: f32 = 0.707106781186547524400844362104849039_f32; + + /// Euler's number + pub static e: f32 = 2.71828182845904523536028747135266250_f32; + + /// log2(e) + pub static log2_e: f32 = 1.44269504088896340735992468100189214_f32; + + /// log10(e) + pub static log10_e: f32 = 0.434294481903251827651128918916605082_f32; + + /// ln(2.0) + pub static ln_2: f32 = 0.693147180559945309417232121458176568_f32; + + /// ln(10.0) + pub static ln_10: f32 = 2.30258509299404568401799145468436421_f32; +} + +impl Num for f32 {} + +#[cfg(not(test))] +impl Eq for f32 { + #[inline(always)] + fn eq(&self, other: &f32) -> bool { (*self) == (*other) } + #[inline(always)] + fn ne(&self, other: &f32) -> bool { (*self) != (*other) } +} + +#[cfg(not(test))] +impl ApproxEq<f32> for f32 { + #[inline(always)] + fn approx_epsilon() -> f32 { 1.0e-6 } + + #[inline(always)] + fn approx_eq(&self, other: &f32) -> bool { + self.approx_eq_eps(other, &ApproxEq::approx_epsilon::<f32, f32>()) + } + + #[inline(always)] + fn approx_eq_eps(&self, other: &f32, approx_epsilon: &f32) -> bool { + (*self - *other).abs() < *approx_epsilon + } +} + +#[cfg(not(test))] +impl Ord for f32 { + #[inline(always)] + fn lt(&self, other: &f32) -> bool { (*self) < (*other) } + #[inline(always)] + fn le(&self, other: &f32) -> bool { (*self) <= (*other) } + #[inline(always)] + fn ge(&self, other: &f32) -> bool { (*self) >= (*other) } + #[inline(always)] + fn gt(&self, other: &f32) -> bool { (*self) > (*other) } +} + +impl Orderable for f32 { + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn min(&self, other: &f32) -> f32 { + if self.is_NaN() || other.is_NaN() { Float::NaN() } else { fmin(*self, *other) } + } + + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn max(&self, other: &f32) -> f32 { + if self.is_NaN() || other.is_NaN() { Float::NaN() } else { fmax(*self, *other) } + } + + /// Returns the number constrained within the range `mn <= self <= mx`. + /// If any of the numbers are `NaN` then `NaN` is returned. + #[inline(always)] + fn clamp(&self, mn: &f32, mx: &f32) -> f32 { + cond!( + (self.is_NaN()) { *self } + (!(*self <= *mx)) { *mx } + (!(*self >= *mn)) { *mn } + _ { *self } + ) + } +} + +impl Zero for f32 { + #[inline(always)] + fn zero() -> f32 { 0.0 } + + /// Returns true if the number is equal to either `0.0` or `-0.0` + #[inline(always)] + fn is_zero(&self) -> bool { *self == 0.0 || *self == -0.0 } +} + +impl One for f32 { + #[inline(always)] + fn one() -> f32 { 1.0 } +} + +#[cfg(not(test))] +impl Add<f32,f32> for f32 { + #[inline(always)] + fn add(&self, other: &f32) -> f32 { *self + *other } +} + +#[cfg(not(test))] +impl Sub<f32,f32> for f32 { + #[inline(always)] + fn sub(&self, other: &f32) -> f32 { *self - *other } +} + +#[cfg(not(test))] +impl Mul<f32,f32> for f32 { + #[inline(always)] + fn mul(&self, other: &f32) -> f32 { *self * *other } +} + +#[cfg(not(test))] +impl Div<f32,f32> for f32 { + #[inline(always)] + fn div(&self, other: &f32) -> f32 { *self / *other } +} + +#[cfg(not(test))] +impl Rem<f32,f32> for f32 { + #[inline(always)] + fn rem(&self, other: &f32) -> f32 { *self % *other } +} + +#[cfg(not(test))] +impl Neg<f32> for f32 { + #[inline(always)] + fn neg(&self) -> f32 { -*self } +} + +impl Signed for f32 { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> f32 { abs(*self) } + + /// + /// The positive difference of two numbers. Returns `0.0` if the number is less than or + /// equal to `other`, otherwise the difference between`self` and `other` is returned. + /// + #[inline(always)] + fn abs_sub(&self, other: &f32) -> f32 { abs_sub(*self, *other) } + + /// + /// # Returns + /// + /// - `1.0` if the number is positive, `+0.0` or `infinity` + /// - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + /// - `NaN` if the number is NaN + /// + #[inline(always)] + fn signum(&self) -> f32 { + if self.is_NaN() { NaN } else { copysign(1.0, *self) } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + +impl Round for f32 { + /// Round half-way cases toward `neg_infinity` + #[inline(always)] + fn floor(&self) -> f32 { floor(*self) } + + /// Round half-way cases toward `infinity` + #[inline(always)] + fn ceil(&self) -> f32 { ceil(*self) } + + /// Round half-way cases away from `0.0` + #[inline(always)] + fn round(&self) -> f32 { round(*self) } + + /// The integer part of the number (rounds towards `0.0`) + #[inline(always)] + fn trunc(&self) -> f32 { trunc(*self) } + + /// + /// The fractional part of the number, satisfying: + /// + /// ~~~ + /// assert!(x == trunc(x) + fract(x)) + /// ~~~ + /// + #[inline(always)] + fn fract(&self) -> f32 { *self - self.trunc() } +} + +impl Fractional for f32 { + /// The reciprocal (multiplicative inverse) of the number + #[inline(always)] + fn recip(&self) -> f32 { 1.0 / *self } +} + +impl Algebraic for f32 { + #[inline(always)] + fn pow(&self, n: f32) -> f32 { pow(*self, n) } + + #[inline(always)] + fn sqrt(&self) -> f32 { sqrt(*self) } + + #[inline(always)] + fn rsqrt(&self) -> f32 { self.sqrt().recip() } + + #[inline(always)] + fn cbrt(&self) -> f32 { cbrt(*self) } + + #[inline(always)] + fn hypot(&self, other: f32) -> f32 { hypot(*self, other) } +} + +impl Trigonometric for f32 { + #[inline(always)] + fn sin(&self) -> f32 { sin(*self) } + + #[inline(always)] + fn cos(&self) -> f32 { cos(*self) } + + #[inline(always)] + fn tan(&self) -> f32 { tan(*self) } + + #[inline(always)] + fn asin(&self) -> f32 { asin(*self) } + + #[inline(always)] + fn acos(&self) -> f32 { acos(*self) } + + #[inline(always)] + fn atan(&self) -> f32 { atan(*self) } + + #[inline(always)] + fn atan2(&self, other: f32) -> f32 { atan2(*self, other) } + + /// Simultaneously computes the sine and cosine of the number + #[inline(always)] + fn sin_cos(&self) -> (f32, f32) { + (self.sin(), self.cos()) + } +} + +impl Exponential for f32 { + /// Returns the exponential of the number + #[inline(always)] + fn exp(&self) -> f32 { exp(*self) } + + /// Returns 2 raised to the power of the number + #[inline(always)] + fn exp2(&self) -> f32 { exp2(*self) } + + /// Returns the natural logarithm of the number + #[inline(always)] + fn ln(&self) -> f32 { ln(*self) } + + /// Returns the logarithm of the number with respect to an arbitrary base + #[inline(always)] + fn log(&self, base: f32) -> f32 { self.ln() / base.ln() } + + /// Returns the base 2 logarithm of the number + #[inline(always)] + fn log2(&self) -> f32 { log2(*self) } + + /// Returns the base 10 logarithm of the number + #[inline(always)] + fn log10(&self) -> f32 { log10(*self) } +} + +impl Hyperbolic for f32 { + #[inline(always)] + fn sinh(&self) -> f32 { sinh(*self) } + + #[inline(always)] + fn cosh(&self) -> f32 { cosh(*self) } + + #[inline(always)] + fn tanh(&self) -> f32 { tanh(*self) } + + /// + /// Inverse hyperbolic sine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic sine of `self` will be returned + /// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity` + /// - `NaN` if `self` is `NaN` + /// + #[inline(always)] + fn asinh(&self) -> f32 { + match *self { + neg_infinity => neg_infinity, + x => (x + ((x * x) + 1.0).sqrt()).ln(), + } + } + + /// + /// Inverse hyperbolic cosine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic cosine of `self` will be returned + /// - `infinity` if `self` is `infinity` + /// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`) + /// + #[inline(always)] + fn acosh(&self) -> f32 { + match *self { + x if x < 1.0 => Float::NaN(), + x => (x + ((x * x) - 1.0).sqrt()).ln(), + } + } + + /// + /// Inverse hyperbolic tangent + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic tangent of `self` will be returned + /// - `self` if `self` is `0.0` or `-0.0` + /// - `infinity` if `self` is `1.0` + /// - `neg_infinity` if `self` is `-1.0` + /// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0` + /// (including `infinity` and `neg_infinity`) + /// + #[inline(always)] + fn atanh(&self) -> f32 { + 0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p() + } +} + +impl Real for f32 { + /// Archimedes' constant + #[inline(always)] + fn pi() -> f32 { 3.14159265358979323846264338327950288 } + + /// 2.0 * pi + #[inline(always)] + fn two_pi() -> f32 { 6.28318530717958647692528676655900576 } + + /// pi / 2.0 + #[inline(always)] + fn frac_pi_2() -> f32 { 1.57079632679489661923132169163975144 } + + /// pi / 3.0 + #[inline(always)] + fn frac_pi_3() -> f32 { 1.04719755119659774615421446109316763 } + + /// pi / 4.0 + #[inline(always)] + fn frac_pi_4() -> f32 { 0.785398163397448309615660845819875721 } + + /// pi / 6.0 + #[inline(always)] + fn frac_pi_6() -> f32 { 0.52359877559829887307710723054658381 } + + /// pi / 8.0 + #[inline(always)] + fn frac_pi_8() -> f32 { 0.39269908169872415480783042290993786 } + + /// 1 .0/ pi + #[inline(always)] + fn frac_1_pi() -> f32 { 0.318309886183790671537767526745028724 } + + /// 2.0 / pi + #[inline(always)] + fn frac_2_pi() -> f32 { 0.636619772367581343075535053490057448 } + + /// 2.0 / sqrt(pi) + #[inline(always)] + fn frac_2_sqrtpi() -> f32 { 1.12837916709551257389615890312154517 } + + /// sqrt(2.0) + #[inline(always)] + fn sqrt2() -> f32 { 1.41421356237309504880168872420969808 } + + /// 1.0 / sqrt(2.0) + #[inline(always)] + fn frac_1_sqrt2() -> f32 { 0.707106781186547524400844362104849039 } + + /// Euler's number + #[inline(always)] + fn e() -> f32 { 2.71828182845904523536028747135266250 } + + /// log2(e) + #[inline(always)] + fn log2_e() -> f32 { 1.44269504088896340735992468100189214 } + + /// log10(e) + #[inline(always)] + fn log10_e() -> f32 { 0.434294481903251827651128918916605082 } + + /// ln(2.0) + #[inline(always)] + fn ln_2() -> f32 { 0.693147180559945309417232121458176568 } + + /// ln(10.0) + #[inline(always)] + fn ln_10() -> f32 { 2.30258509299404568401799145468436421 } + + /// Converts to degrees, assuming the number is in radians + #[inline(always)] + fn to_degrees(&self) -> f32 { *self * (180.0 / Real::pi::<f32>()) } + + /// Converts to radians, assuming the number is in degrees + #[inline(always)] + fn to_radians(&self) -> f32 { *self * (Real::pi::<f32>() / 180.0) } +} + +impl Bounded for f32 { + #[inline(always)] + fn min_value() -> f32 { 1.17549435e-38 } + + #[inline(always)] + fn max_value() -> f32 { 3.40282347e+38 } +} + +impl Primitive for f32 { + #[inline(always)] + fn bits() -> uint { 32 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<f32>() / 8 } +} + +impl Float for f32 { + #[inline(always)] + fn NaN() -> f32 { 0.0 / 0.0 } + + #[inline(always)] + fn infinity() -> f32 { 1.0 / 0.0 } + + #[inline(always)] + fn neg_infinity() -> f32 { -1.0 / 0.0 } + + #[inline(always)] + fn neg_zero() -> f32 { -0.0 } + + /// Returns `true` if the number is NaN + #[inline(always)] + fn is_NaN(&self) -> bool { *self != *self } + + /// Returns `true` if the number is infinite + #[inline(always)] + fn is_infinite(&self) -> bool { + *self == Float::infinity() || *self == Float::neg_infinity() + } + + /// Returns `true` if the number is neither infinite or NaN + #[inline(always)] + fn is_finite(&self) -> bool { + !(self.is_NaN() || self.is_infinite()) + } + + /// Returns `true` if the number is neither zero, infinite, subnormal or NaN + #[inline(always)] + fn is_normal(&self) -> bool { + self.classify() == FPNormal + } + + /// Returns the floating point category of the number. If only one property is going to + /// be tested, it is generally faster to use the specific predicate instead. + fn classify(&self) -> FPCategory { + static EXP_MASK: u32 = 0x7f800000; + static MAN_MASK: u32 = 0x007fffff; + + match ( + unsafe { ::cast::transmute::<f32,u32>(*self) } & MAN_MASK, + unsafe { ::cast::transmute::<f32,u32>(*self) } & EXP_MASK, + ) { + (0, 0) => FPZero, + (_, 0) => FPSubnormal, + (0, EXP_MASK) => FPInfinite, + (_, EXP_MASK) => FPNaN, + _ => FPNormal, + } + } + + #[inline(always)] + fn mantissa_digits() -> uint { 24 } + + #[inline(always)] + fn digits() -> uint { 6 } + + #[inline(always)] + fn epsilon() -> f32 { 1.19209290e-07 } + + #[inline(always)] + fn min_exp() -> int { -125 } + + #[inline(always)] + fn max_exp() -> int { 128 } + + #[inline(always)] + fn min_10_exp() -> int { -37 } + + #[inline(always)] + fn max_10_exp() -> int { 38 } + + /// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp` + #[inline(always)] + fn ldexp(x: f32, exp: int) -> f32 { + ldexp(x, exp as c_int) + } + + /// + /// Breaks the number into a normalized fraction and a base-2 exponent, satisfying: + /// + /// - `self = x * pow(2, exp)` + /// - `0.5 <= abs(x) < 1.0` + /// + #[inline(always)] + fn frexp(&self) -> (f32, int) { + let mut exp = 0; + let x = frexp(*self, &mut exp); + (x, exp as int) + } + + /// + /// Returns the exponential of the number, minus `1`, in a way that is accurate + /// even if the number is close to zero + /// + #[inline(always)] + fn exp_m1(&self) -> f32 { exp_m1(*self) } + + /// + /// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately + /// than if the operations were performed separately + /// + #[inline(always)] + fn ln_1p(&self) -> f32 { ln_1p(*self) } + + /// + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This + /// produces a more accurate result with better performance than a separate multiplication + /// operation followed by an add. + /// + #[inline(always)] + fn mul_add(&self, a: f32, b: f32) -> f32 { + mul_add(*self, a, b) + } + + /// Returns the next representable floating-point value in the direction of `other` + #[inline(always)] + fn next_after(&self, other: f32) -> f32 { + next_after(*self, other) + } +} + +// +// Section: String Conversions +// + +/// +/// Converts a float to a string +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str(num: f32) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in hexadecimal format +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str_hex(num: f32) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in a given radix +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +/// # Failure +/// +/// Fails if called on a special value like `inf`, `-inf` or `NaN` due to +/// possible misinterpretation of the result at higher bases. If those values +/// are expected, use `to_str_radix_special()` instead. +/// +#[inline(always)] +pub fn to_str_radix(num: f32, rdx: uint) -> ~str { + let (r, special) = strconv::to_str_common( + &num, rdx, true, strconv::SignNeg, strconv::DigAll); + if special { fail!("number has a special value, \ + try to_str_radix_special() if those are expected") } + r +} + +/// +/// Converts a float to a string in a given radix, and a flag indicating +/// whether it's a special value +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +#[inline(always)] +pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { + strconv::to_str_common(&num, rdx, true, + strconv::SignNeg, strconv::DigAll) +} + +/// +/// Converts a float to a string with exactly the number of +/// provided significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_exact(num: f32, dig: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); + r +} + +/// +/// Converts a float to a string with a maximum number of +/// significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_digits(num: f32, dig: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); + r +} + +impl to_str::ToStr for f32 { + #[inline(always)] + fn to_str(&self) -> ~str { to_str_digits(*self, 8) } +} + +impl num::ToStrRadix for f32 { + #[inline(always)] + fn to_str_radix(&self, rdx: uint) -> ~str { + to_str_radix(*self, rdx) + } +} + +/// +/// Convert a string in base 10 to a float. +/// Accepts a optional decimal exponent. +/// +/// This function accepts strings such as +/// +/// * '3.14' +/// * '+3.14', equivalent to '3.14' +/// * '-3.14' +/// * '2.5E10', or equivalently, '2.5e10' +/// * '2.5E-10' +/// * '.' (understood as 0) +/// * '5.' +/// * '.5', or, equivalently, '0.5' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str(num: &str) -> Option<f32> { + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false, false) +} + +/// +/// Convert a string in base 16 to a float. +/// Accepts a optional binary exponent. +/// +/// This function accepts strings such as +/// +/// * 'a4.fe' +/// * '+a4.fe', equivalent to 'a4.fe' +/// * '-a4.fe' +/// * '2b.aP128', or equivalently, '2b.ap128' +/// * '2b.aP-128' +/// * '.' (understood as 0) +/// * 'c.' +/// * '.c', or, equivalently, '0.c' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `[num]`. +/// +#[inline(always)] +pub fn from_str_hex(num: &str) -> Option<f32> { + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false, false) +} + +/// +/// Convert a string in an given base to a float. +/// +/// Due to possible conflicts, this function does **not** accept +/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** +/// does it recognize exponents of any kind. +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// * radix - The base to use. Must lie in the range [2 .. 36] +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str_radix(num: &str, rdx: uint) -> Option<f32> { + strconv::from_str_common(num, rdx, true, true, false, + strconv::ExpNone, false, false) +} + +impl FromStr for f32 { + #[inline(always)] + fn from_str(val: &str) -> Option<f32> { from_str(val) } +} + +impl num::FromStrRadix for f32 { + #[inline(always)] + fn from_str_radix(val: &str, rdx: uint) -> Option<f32> { + from_str_radix(val, rdx) + } +} + +#[cfg(test)] +mod tests { + use f32::*; + use num::*; + use super::*; + use prelude::*; + + #[test] + fn test_num() { + num::test_num(10f32, 2f32); + } + + #[test] + fn test_min() { + assert_eq!(1f32.min(&2f32), 1f32); + assert_eq!(2f32.min(&1f32), 1f32); + } + + #[test] + fn test_max() { + assert_eq!(1f32.max(&2f32), 2f32); + assert_eq!(2f32.max(&1f32), 2f32); + } + + #[test] + fn test_clamp() { + assert_eq!(1f32.clamp(&2f32, &4f32), 2f32); + assert_eq!(8f32.clamp(&2f32, &4f32), 4f32); + assert_eq!(3f32.clamp(&2f32, &4f32), 3f32); + assert!(3f32.clamp(&Float::NaN::<f32>(), &4f32).is_NaN()); + assert!(3f32.clamp(&2f32, &Float::NaN::<f32>()).is_NaN()); + assert!(Float::NaN::<f32>().clamp(&2f32, &4f32).is_NaN()); + } + + #[test] + fn test_floor() { + assert_approx_eq!(1.0f32.floor(), 1.0f32); + assert_approx_eq!(1.3f32.floor(), 1.0f32); + assert_approx_eq!(1.5f32.floor(), 1.0f32); + assert_approx_eq!(1.7f32.floor(), 1.0f32); + assert_approx_eq!(0.0f32.floor(), 0.0f32); + assert_approx_eq!((-0.0f32).floor(), -0.0f32); + assert_approx_eq!((-1.0f32).floor(), -1.0f32); + assert_approx_eq!((-1.3f32).floor(), -2.0f32); + assert_approx_eq!((-1.5f32).floor(), -2.0f32); + assert_approx_eq!((-1.7f32).floor(), -2.0f32); + } + + #[test] + fn test_ceil() { + assert_approx_eq!(1.0f32.ceil(), 1.0f32); + assert_approx_eq!(1.3f32.ceil(), 2.0f32); + assert_approx_eq!(1.5f32.ceil(), 2.0f32); + assert_approx_eq!(1.7f32.ceil(), 2.0f32); + assert_approx_eq!(0.0f32.ceil(), 0.0f32); + assert_approx_eq!((-0.0f32).ceil(), -0.0f32); + assert_approx_eq!((-1.0f32).ceil(), -1.0f32); + assert_approx_eq!((-1.3f32).ceil(), -1.0f32); + assert_approx_eq!((-1.5f32).ceil(), -1.0f32); + assert_approx_eq!((-1.7f32).ceil(), -1.0f32); + } + + #[test] + fn test_round() { + assert_approx_eq!(1.0f32.round(), 1.0f32); + assert_approx_eq!(1.3f32.round(), 1.0f32); + assert_approx_eq!(1.5f32.round(), 2.0f32); + assert_approx_eq!(1.7f32.round(), 2.0f32); + assert_approx_eq!(0.0f32.round(), 0.0f32); + assert_approx_eq!((-0.0f32).round(), -0.0f32); + assert_approx_eq!((-1.0f32).round(), -1.0f32); + assert_approx_eq!((-1.3f32).round(), -1.0f32); + assert_approx_eq!((-1.5f32).round(), -2.0f32); + assert_approx_eq!((-1.7f32).round(), -2.0f32); + } + + #[test] + fn test_trunc() { + assert_approx_eq!(1.0f32.trunc(), 1.0f32); + assert_approx_eq!(1.3f32.trunc(), 1.0f32); + assert_approx_eq!(1.5f32.trunc(), 1.0f32); + assert_approx_eq!(1.7f32.trunc(), 1.0f32); + assert_approx_eq!(0.0f32.trunc(), 0.0f32); + assert_approx_eq!((-0.0f32).trunc(), -0.0f32); + assert_approx_eq!((-1.0f32).trunc(), -1.0f32); + assert_approx_eq!((-1.3f32).trunc(), -1.0f32); + assert_approx_eq!((-1.5f32).trunc(), -1.0f32); + assert_approx_eq!((-1.7f32).trunc(), -1.0f32); + } + + #[test] + fn test_fract() { + assert_approx_eq!(1.0f32.fract(), 0.0f32); + assert_approx_eq!(1.3f32.fract(), 0.3f32); + assert_approx_eq!(1.5f32.fract(), 0.5f32); + assert_approx_eq!(1.7f32.fract(), 0.7f32); + assert_approx_eq!(0.0f32.fract(), 0.0f32); + assert_approx_eq!((-0.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.3f32).fract(), -0.3f32); + assert_approx_eq!((-1.5f32).fract(), -0.5f32); + assert_approx_eq!((-1.7f32).fract(), -0.7f32); + } + + #[test] + fn test_asinh() { + assert_eq!(0.0f32.asinh(), 0.0f32); + assert_eq!((-0.0f32).asinh(), -0.0f32); + assert_eq!(Float::infinity::<f32>().asinh(), Float::infinity::<f32>()); + assert_eq!(Float::neg_infinity::<f32>().asinh(), Float::neg_infinity::<f32>()); + assert!(Float::NaN::<f32>().asinh().is_NaN()); + assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); + assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + } + + #[test] + fn test_acosh() { + assert_eq!(1.0f32.acosh(), 0.0f32); + assert!(0.999f32.acosh().is_NaN()); + assert_eq!(Float::infinity::<f32>().acosh(), Float::infinity::<f32>()); + assert!(Float::neg_infinity::<f32>().acosh().is_NaN()); + assert!(Float::NaN::<f32>().acosh().is_NaN()); + assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); + assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); + } + + #[test] + fn test_atanh() { + assert_eq!(0.0f32.atanh(), 0.0f32); + assert_eq!((-0.0f32).atanh(), -0.0f32); + assert_eq!(1.0f32.atanh(), Float::infinity::<f32>()); + assert_eq!((-1.0f32).atanh(), Float::neg_infinity::<f32>()); + assert!(2f64.atanh().atanh().is_NaN()); + assert!((-2f64).atanh().atanh().is_NaN()); + assert!(Float::infinity::<f64>().atanh().is_NaN()); + assert!(Float::neg_infinity::<f64>().atanh().is_NaN()); + assert!(Float::NaN::<f32>().atanh().is_NaN()); + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); + assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); + } + + #[test] + fn test_real_consts() { + assert_approx_eq!(Real::two_pi::<f32>(), 2f32 * Real::pi::<f32>()); + assert_approx_eq!(Real::frac_pi_2::<f32>(), Real::pi::<f32>() / 2f32); + assert_approx_eq!(Real::frac_pi_3::<f32>(), Real::pi::<f32>() / 3f32); + assert_approx_eq!(Real::frac_pi_4::<f32>(), Real::pi::<f32>() / 4f32); + assert_approx_eq!(Real::frac_pi_6::<f32>(), Real::pi::<f32>() / 6f32); + assert_approx_eq!(Real::frac_pi_8::<f32>(), Real::pi::<f32>() / 8f32); + assert_approx_eq!(Real::frac_1_pi::<f32>(), 1f32 / Real::pi::<f32>()); + assert_approx_eq!(Real::frac_2_pi::<f32>(), 2f32 / Real::pi::<f32>()); + assert_approx_eq!(Real::frac_2_sqrtpi::<f32>(), 2f32 / Real::pi::<f32>().sqrt()); + assert_approx_eq!(Real::sqrt2::<f32>(), 2f32.sqrt()); + assert_approx_eq!(Real::frac_1_sqrt2::<f32>(), 1f32 / 2f32.sqrt()); + assert_approx_eq!(Real::log2_e::<f32>(), Real::e::<f32>().log2()); + assert_approx_eq!(Real::log10_e::<f32>(), Real::e::<f32>().log10()); + assert_approx_eq!(Real::ln_2::<f32>(), 2f32.ln()); + assert_approx_eq!(Real::ln_10::<f32>(), 10f32.ln()); + } + + #[test] + pub fn test_abs() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f32.abs(), 1f32); + assert_eq!(0f32.abs(), 0f32); + assert_eq!((-0f32).abs(), 0f32); + assert_eq!((-1f32).abs(), 1f32); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f32/neg_infinity).abs(), 0f32); + assert!(NaN.abs().is_NaN()); + } + + #[test] + fn test_abs_sub() { + assert_eq!((-1f32).abs_sub(&1f32), 0f32); + assert_eq!(1f32.abs_sub(&1f32), 0f32); + assert_eq!(1f32.abs_sub(&0f32), 1f32); + assert_eq!(1f32.abs_sub(&-1f32), 2f32); + assert_eq!(neg_infinity.abs_sub(&0f32), 0f32); + assert_eq!(infinity.abs_sub(&1f32), infinity); + assert_eq!(0f32.abs_sub(&neg_infinity), infinity); + assert_eq!(0f32.abs_sub(&infinity), 0f32); + assert!(NaN.abs_sub(&-1f32).is_NaN()); + assert!(1f32.abs_sub(&NaN).is_NaN()); + } + + #[test] + fn test_signum() { + assert_eq!(infinity.signum(), 1f32); + assert_eq!(1f32.signum(), 1f32); + assert_eq!(0f32.signum(), 1f32); + assert_eq!((-0f32).signum(), -1f32); + assert_eq!((-1f32).signum(), -1f32); + assert_eq!(neg_infinity.signum(), -1f32); + assert_eq!((1f32/neg_infinity).signum(), -1f32); + assert!(NaN.signum().is_NaN()); + } + + #[test] + fn test_is_positive() { + assert!(infinity.is_positive()); + assert!(1f32.is_positive()); + assert!(0f32.is_positive()); + assert!(!(-0f32).is_positive()); + assert!(!(-1f32).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f32/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + } + + #[test] + fn test_is_negative() { + assert!(!infinity.is_negative()); + assert!(!1f32.is_negative()); + assert!(!0f32.is_negative()); + assert!((-0f32).is_negative()); + assert!((-1f32).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f32/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } + + #[test] + fn test_approx_eq() { + assert!(1.0f32.approx_eq(&1f32)); + assert!(0.9999999f32.approx_eq(&1f32)); + assert!(1.000001f32.approx_eq_eps(&1f32, &1.0e-5)); + assert!(1.0000001f32.approx_eq_eps(&1f32, &1.0e-6)); + assert!(!1.0000001f32.approx_eq_eps(&1f32, &1.0e-7)); + } + + #[test] + fn test_primitive() { + assert_eq!(Primitive::bits::<f32>(), sys::size_of::<f32>() * 8); + assert_eq!(Primitive::bytes::<f32>(), sys::size_of::<f32>()); + } + + #[test] + fn test_is_normal() { + assert!(!Float::NaN::<f32>().is_normal()); + assert!(!Float::infinity::<f32>().is_normal()); + assert!(!Float::neg_infinity::<f32>().is_normal()); + assert!(!Zero::zero::<f32>().is_normal()); + assert!(!Float::neg_zero::<f32>().is_normal()); + assert!(1f32.is_normal()); + assert!(1e-37f32.is_normal()); + assert!(!1e-38f32.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::<f32>().classify(), FPNaN); + assert_eq!(Float::infinity::<f32>().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::<f32>().classify(), FPInfinite); + assert_eq!(Zero::zero::<f32>().classify(), FPZero); + assert_eq!(Float::neg_zero::<f32>().classify(), FPZero); + assert_eq!(1f32.classify(), FPNormal); + assert_eq!(1e-37f32.classify(), FPNormal); + assert_eq!(1e-38f32.classify(), FPSubnormal); + } + + #[test] + fn test_ldexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: f32 = from_str_hex("1p-123").unwrap(); + let f2: f32 = from_str_hex("1p-111").unwrap(); + assert_eq!(Float::ldexp(1f32, -123), f1); + assert_eq!(Float::ldexp(1f32, -111), f2); + + assert_eq!(Float::ldexp(0f32, -123), 0f32); + assert_eq!(Float::ldexp(-0f32, -123), -0f32); + assert_eq!(Float::ldexp(Float::infinity::<f32>(), -123), + Float::infinity::<f32>()); + assert_eq!(Float::ldexp(Float::neg_infinity::<f32>(), -123), + Float::neg_infinity::<f32>()); + assert!(Float::ldexp(Float::NaN::<f32>(), -123).is_NaN()); + } + + #[test] + fn test_frexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: f32 = from_str_hex("1p-123").unwrap(); + let f2: f32 = from_str_hex("1p-111").unwrap(); + let (x1, exp1) = f1.frexp(); + let (x2, exp2) = f2.frexp(); + assert_eq!((x1, exp1), (0.5f32, -122)); + assert_eq!((x2, exp2), (0.5f32, -110)); + assert_eq!(Float::ldexp(x1, exp1), f1); + assert_eq!(Float::ldexp(x2, exp2), f2); + + assert_eq!(0f32.frexp(), (0f32, 0)); + assert_eq!((-0f32).frexp(), (-0f32, 0)); + assert_eq!(match Float::infinity::<f32>().frexp() { (x, _) => x }, + Float::infinity::<f32>()) + assert_eq!(match Float::neg_infinity::<f32>().frexp() { (x, _) => x }, + Float::neg_infinity::<f32>()) + assert!(match Float::NaN::<f32>().frexp() { (x, _) => x.is_NaN() }) + } +} diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs new file mode 100644 index 00000000000..6e2496e2e45 --- /dev/null +++ b/src/libstd/num/f64.rs @@ -0,0 +1,1295 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `f64` + +use libc::c_int; +use num::{Zero, One, strconv}; +use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal}; +use prelude::*; + +pub use cmath::c_double_targ_consts::*; +pub use cmp::{min, max}; + +// An inner module is required to get the #[inline(always)] attribute on the +// functions. +pub use self::delegated::*; + +macro_rules! delegate( + ( + $( + fn $name:ident( + $( + $arg:ident : $arg_ty:ty + ),* + ) -> $rv:ty = $bound_name:path + ),* + ) => ( + mod delegated { + use cmath::c_double_utils; + use libc::{c_double, c_int}; + use unstable::intrinsics; + + $( + #[inline(always)] + pub fn $name($( $arg : $arg_ty ),*) -> $rv { + unsafe { + $bound_name($( $arg ),*) + } + } + )* + } + ) +) + +delegate!( + // intrinsics + fn abs(n: f64) -> f64 = intrinsics::fabsf64, + fn cos(n: f64) -> f64 = intrinsics::cosf64, + fn exp(n: f64) -> f64 = intrinsics::expf64, + fn exp2(n: f64) -> f64 = intrinsics::exp2f64, + fn floor(x: f64) -> f64 = intrinsics::floorf64, + fn ln(n: f64) -> f64 = intrinsics::logf64, + fn log10(n: f64) -> f64 = intrinsics::log10f64, + fn log2(n: f64) -> f64 = intrinsics::log2f64, + fn mul_add(a: f64, b: f64, c: f64) -> f64 = intrinsics::fmaf64, + fn pow(n: f64, e: f64) -> f64 = intrinsics::powf64, + fn powi(n: f64, e: c_int) -> f64 = intrinsics::powif64, + fn sin(n: f64) -> f64 = intrinsics::sinf64, + fn sqrt(n: f64) -> f64 = intrinsics::sqrtf64, + + // LLVM 3.3 required to use intrinsics for these four + fn ceil(n: c_double) -> c_double = c_double_utils::ceil, + fn trunc(n: c_double) -> c_double = c_double_utils::trunc, + /* + fn ceil(n: f64) -> f64 = intrinsics::ceilf64, + fn trunc(n: f64) -> f64 = intrinsics::truncf64, + fn rint(n: c_double) -> c_double = intrinsics::rintf64, + fn nearbyint(n: c_double) -> c_double = intrinsics::nearbyintf64, + */ + + // cmath + fn acos(n: c_double) -> c_double = c_double_utils::acos, + fn asin(n: c_double) -> c_double = c_double_utils::asin, + fn atan(n: c_double) -> c_double = c_double_utils::atan, + fn atan2(a: c_double, b: c_double) -> c_double = c_double_utils::atan2, + fn cbrt(n: c_double) -> c_double = c_double_utils::cbrt, + fn copysign(x: c_double, y: c_double) -> c_double = c_double_utils::copysign, + fn cosh(n: c_double) -> c_double = c_double_utils::cosh, + fn erf(n: c_double) -> c_double = c_double_utils::erf, + fn erfc(n: c_double) -> c_double = c_double_utils::erfc, + fn exp_m1(n: c_double) -> c_double = c_double_utils::exp_m1, + fn abs_sub(a: c_double, b: c_double) -> c_double = c_double_utils::abs_sub, + fn fmax(a: c_double, b: c_double) -> c_double = c_double_utils::fmax, + fn fmin(a: c_double, b: c_double) -> c_double = c_double_utils::fmin, + fn next_after(x: c_double, y: c_double) -> c_double = c_double_utils::next_after, + fn frexp(n: c_double, value: &mut c_int) -> c_double = c_double_utils::frexp, + fn hypot(x: c_double, y: c_double) -> c_double = c_double_utils::hypot, + fn ldexp(x: c_double, n: c_int) -> c_double = c_double_utils::ldexp, + fn lgamma(n: c_double, sign: &mut c_int) -> c_double = c_double_utils::lgamma, + fn log_radix(n: c_double) -> c_double = c_double_utils::log_radix, + fn ln_1p(n: c_double) -> c_double = c_double_utils::ln_1p, + fn ilog_radix(n: c_double) -> c_int = c_double_utils::ilog_radix, + fn modf(n: c_double, iptr: &mut c_double) -> c_double = c_double_utils::modf, + fn round(n: c_double) -> c_double = c_double_utils::round, + fn ldexp_radix(n: c_double, i: c_int) -> c_double = c_double_utils::ldexp_radix, + fn sinh(n: c_double) -> c_double = c_double_utils::sinh, + fn tan(n: c_double) -> c_double = c_double_utils::tan, + fn tanh(n: c_double) -> c_double = c_double_utils::tanh, + fn tgamma(n: c_double) -> c_double = c_double_utils::tgamma, + fn j0(n: c_double) -> c_double = c_double_utils::j0, + fn j1(n: c_double) -> c_double = c_double_utils::j1, + fn jn(i: c_int, n: c_double) -> c_double = c_double_utils::jn, + fn y0(n: c_double) -> c_double = c_double_utils::y0, + fn y1(n: c_double) -> c_double = c_double_utils::y1, + fn yn(i: c_int, n: c_double) -> c_double = c_double_utils::yn +) + +// FIXME (#1433): obtain these in a different way + +// These are not defined inside consts:: for consistency with +// the integer types + +pub static radix: uint = 2u; + +pub static mantissa_digits: uint = 53u; +pub static digits: uint = 15u; + +pub static epsilon: f64 = 2.2204460492503131e-16_f64; + +pub static min_value: f64 = 2.2250738585072014e-308_f64; +pub static max_value: f64 = 1.7976931348623157e+308_f64; + +pub static min_exp: int = -1021; +pub static max_exp: int = 1024; + +pub static min_10_exp: int = -307; +pub static max_10_exp: int = 308; + +pub static NaN: f64 = 0.0_f64/0.0_f64; + +pub static infinity: f64 = 1.0_f64/0.0_f64; + +pub static neg_infinity: f64 = -1.0_f64/0.0_f64; + +#[inline(always)] +pub fn add(x: f64, y: f64) -> f64 { return x + y; } + +#[inline(always)] +pub fn sub(x: f64, y: f64) -> f64 { return x - y; } + +#[inline(always)] +pub fn mul(x: f64, y: f64) -> f64 { return x * y; } + +#[inline(always)] +pub fn div(x: f64, y: f64) -> f64 { return x / y; } + +#[inline(always)] +pub fn rem(x: f64, y: f64) -> f64 { return x % y; } + +#[inline(always)] +pub fn lt(x: f64, y: f64) -> bool { return x < y; } + +#[inline(always)] +pub fn le(x: f64, y: f64) -> bool { return x <= y; } + +#[inline(always)] +pub fn eq(x: f64, y: f64) -> bool { return x == y; } + +#[inline(always)] +pub fn ne(x: f64, y: f64) -> bool { return x != y; } + +#[inline(always)] +pub fn ge(x: f64, y: f64) -> bool { return x >= y; } + +#[inline(always)] +pub fn gt(x: f64, y: f64) -> bool { return x > y; } + + +// FIXME (#1999): add is_normal, is_subnormal, and fpclassify + +/* Module: consts */ +pub mod consts { + // FIXME (requires Issue #1433 to fix): replace with mathematical + // constants from cmath. + /// Archimedes' constant + pub static pi: f64 = 3.14159265358979323846264338327950288_f64; + + /// pi/2.0 + pub static frac_pi_2: f64 = 1.57079632679489661923132169163975144_f64; + + /// pi/4.0 + pub static frac_pi_4: f64 = 0.785398163397448309615660845819875721_f64; + + /// 1.0/pi + pub static frac_1_pi: f64 = 0.318309886183790671537767526745028724_f64; + + /// 2.0/pi + pub static frac_2_pi: f64 = 0.636619772367581343075535053490057448_f64; + + /// 2.0/sqrt(pi) + pub static frac_2_sqrtpi: f64 = 1.12837916709551257389615890312154517_f64; + + /// sqrt(2.0) + pub static sqrt2: f64 = 1.41421356237309504880168872420969808_f64; + + /// 1.0/sqrt(2.0) + pub static frac_1_sqrt2: f64 = 0.707106781186547524400844362104849039_f64; + + /// Euler's number + pub static e: f64 = 2.71828182845904523536028747135266250_f64; + + /// log2(e) + pub static log2_e: f64 = 1.44269504088896340735992468100189214_f64; + + /// log10(e) + pub static log10_e: f64 = 0.434294481903251827651128918916605082_f64; + + /// ln(2.0) + pub static ln_2: f64 = 0.693147180559945309417232121458176568_f64; + + /// ln(10.0) + pub static ln_10: f64 = 2.30258509299404568401799145468436421_f64; +} + +impl Num for f64 {} + +#[cfg(not(test))] +impl Eq for f64 { + #[inline(always)] + fn eq(&self, other: &f64) -> bool { (*self) == (*other) } + #[inline(always)] + fn ne(&self, other: &f64) -> bool { (*self) != (*other) } +} + +#[cfg(not(test))] +impl ApproxEq<f64> for f64 { + #[inline(always)] + fn approx_epsilon() -> f64 { 1.0e-6 } + + #[inline(always)] + fn approx_eq(&self, other: &f64) -> bool { + self.approx_eq_eps(other, &ApproxEq::approx_epsilon::<f64, f64>()) + } + + #[inline(always)] + fn approx_eq_eps(&self, other: &f64, approx_epsilon: &f64) -> bool { + (*self - *other).abs() < *approx_epsilon + } +} + +#[cfg(not(test))] +impl Ord for f64 { + #[inline(always)] + fn lt(&self, other: &f64) -> bool { (*self) < (*other) } + #[inline(always)] + fn le(&self, other: &f64) -> bool { (*self) <= (*other) } + #[inline(always)] + fn ge(&self, other: &f64) -> bool { (*self) >= (*other) } + #[inline(always)] + fn gt(&self, other: &f64) -> bool { (*self) > (*other) } +} + +impl Orderable for f64 { + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn min(&self, other: &f64) -> f64 { + if self.is_NaN() || other.is_NaN() { Float::NaN() } else { fmin(*self, *other) } + } + + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn max(&self, other: &f64) -> f64 { + if self.is_NaN() || other.is_NaN() { Float::NaN() } else { fmax(*self, *other) } + } + + /// Returns the number constrained within the range `mn <= self <= mx`. + /// If any of the numbers are `NaN` then `NaN` is returned. + #[inline(always)] + fn clamp(&self, mn: &f64, mx: &f64) -> f64 { + cond!( + (self.is_NaN()) { *self } + (!(*self <= *mx)) { *mx } + (!(*self >= *mn)) { *mn } + _ { *self } + ) + } +} + +impl Zero for f64 { + #[inline(always)] + fn zero() -> f64 { 0.0 } + + /// Returns true if the number is equal to either `0.0` or `-0.0` + #[inline(always)] + fn is_zero(&self) -> bool { *self == 0.0 || *self == -0.0 } +} + +impl One for f64 { + #[inline(always)] + fn one() -> f64 { 1.0 } +} + +#[cfg(not(test))] +impl Add<f64,f64> for f64 { + fn add(&self, other: &f64) -> f64 { *self + *other } +} +#[cfg(not(test))] +impl Sub<f64,f64> for f64 { + fn sub(&self, other: &f64) -> f64 { *self - *other } +} +#[cfg(not(test))] +impl Mul<f64,f64> for f64 { + fn mul(&self, other: &f64) -> f64 { *self * *other } +} +#[cfg(not(test))] +impl Div<f64,f64> for f64 { + fn div(&self, other: &f64) -> f64 { *self / *other } +} +#[cfg(not(test))] +impl Rem<f64,f64> for f64 { + #[inline(always)] + fn rem(&self, other: &f64) -> f64 { *self % *other } +} +#[cfg(not(test))] +impl Neg<f64> for f64 { + fn neg(&self) -> f64 { -*self } +} + +impl Signed for f64 { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> f64 { abs(*self) } + + /// + /// The positive difference of two numbers. Returns `0.0` if the number is less than or + /// equal to `other`, otherwise the difference between`self` and `other` is returned. + /// + #[inline(always)] + fn abs_sub(&self, other: &f64) -> f64 { abs_sub(*self, *other) } + + /// + /// # Returns + /// + /// - `1.0` if the number is positive, `+0.0` or `infinity` + /// - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + /// - `NaN` if the number is NaN + /// + #[inline(always)] + fn signum(&self) -> f64 { + if self.is_NaN() { NaN } else { copysign(1.0, *self) } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + +impl Round for f64 { + /// Round half-way cases toward `neg_infinity` + #[inline(always)] + fn floor(&self) -> f64 { floor(*self) } + + /// Round half-way cases toward `infinity` + #[inline(always)] + fn ceil(&self) -> f64 { ceil(*self) } + + /// Round half-way cases away from `0.0` + #[inline(always)] + fn round(&self) -> f64 { round(*self) } + + /// The integer part of the number (rounds towards `0.0`) + #[inline(always)] + fn trunc(&self) -> f64 { trunc(*self) } + + /// + /// The fractional part of the number, satisfying: + /// + /// ~~~ + /// assert!(x == trunc(x) + fract(x)) + /// ~~~ + /// + #[inline(always)] + fn fract(&self) -> f64 { *self - self.trunc() } +} + +impl Fractional for f64 { + /// The reciprocal (multiplicative inverse) of the number + #[inline(always)] + fn recip(&self) -> f64 { 1.0 / *self } +} + +impl Algebraic for f64 { + #[inline(always)] + fn pow(&self, n: f64) -> f64 { pow(*self, n) } + + #[inline(always)] + fn sqrt(&self) -> f64 { sqrt(*self) } + + #[inline(always)] + fn rsqrt(&self) -> f64 { self.sqrt().recip() } + + #[inline(always)] + fn cbrt(&self) -> f64 { cbrt(*self) } + + #[inline(always)] + fn hypot(&self, other: f64) -> f64 { hypot(*self, other) } +} + +impl Trigonometric for f64 { + #[inline(always)] + fn sin(&self) -> f64 { sin(*self) } + + #[inline(always)] + fn cos(&self) -> f64 { cos(*self) } + + #[inline(always)] + fn tan(&self) -> f64 { tan(*self) } + + #[inline(always)] + fn asin(&self) -> f64 { asin(*self) } + + #[inline(always)] + fn acos(&self) -> f64 { acos(*self) } + + #[inline(always)] + fn atan(&self) -> f64 { atan(*self) } + + #[inline(always)] + fn atan2(&self, other: f64) -> f64 { atan2(*self, other) } + + /// Simultaneously computes the sine and cosine of the number + #[inline(always)] + fn sin_cos(&self) -> (f64, f64) { + (self.sin(), self.cos()) + } +} + +impl Exponential for f64 { + /// Returns the exponential of the number + #[inline(always)] + fn exp(&self) -> f64 { exp(*self) } + + /// Returns 2 raised to the power of the number + #[inline(always)] + fn exp2(&self) -> f64 { exp2(*self) } + + /// Returns the natural logarithm of the number + #[inline(always)] + fn ln(&self) -> f64 { ln(*self) } + + /// Returns the logarithm of the number with respect to an arbitrary base + #[inline(always)] + fn log(&self, base: f64) -> f64 { self.ln() / base.ln() } + + /// Returns the base 2 logarithm of the number + #[inline(always)] + fn log2(&self) -> f64 { log2(*self) } + + /// Returns the base 10 logarithm of the number + #[inline(always)] + fn log10(&self) -> f64 { log10(*self) } +} + +impl Hyperbolic for f64 { + #[inline(always)] + fn sinh(&self) -> f64 { sinh(*self) } + + #[inline(always)] + fn cosh(&self) -> f64 { cosh(*self) } + + #[inline(always)] + fn tanh(&self) -> f64 { tanh(*self) } + + /// + /// Inverse hyperbolic sine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic sine of `self` will be returned + /// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity` + /// - `NaN` if `self` is `NaN` + /// + #[inline(always)] + fn asinh(&self) -> f64 { + match *self { + neg_infinity => neg_infinity, + x => (x + ((x * x) + 1.0).sqrt()).ln(), + } + } + + /// + /// Inverse hyperbolic cosine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic cosine of `self` will be returned + /// - `infinity` if `self` is `infinity` + /// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`) + /// + #[inline(always)] + fn acosh(&self) -> f64 { + match *self { + x if x < 1.0 => Float::NaN(), + x => (x + ((x * x) - 1.0).sqrt()).ln(), + } + } + + /// + /// Inverse hyperbolic tangent + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic tangent of `self` will be returned + /// - `self` if `self` is `0.0` or `-0.0` + /// - `infinity` if `self` is `1.0` + /// - `neg_infinity` if `self` is `-1.0` + /// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0` + /// (including `infinity` and `neg_infinity`) + /// + #[inline(always)] + fn atanh(&self) -> f64 { + 0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p() + } +} + +impl Real for f64 { + /// Archimedes' constant + #[inline(always)] + fn pi() -> f64 { 3.14159265358979323846264338327950288 } + + /// 2.0 * pi + #[inline(always)] + fn two_pi() -> f64 { 6.28318530717958647692528676655900576 } + + /// pi / 2.0 + #[inline(always)] + fn frac_pi_2() -> f64 { 1.57079632679489661923132169163975144 } + + /// pi / 3.0 + #[inline(always)] + fn frac_pi_3() -> f64 { 1.04719755119659774615421446109316763 } + + /// pi / 4.0 + #[inline(always)] + fn frac_pi_4() -> f64 { 0.785398163397448309615660845819875721 } + + /// pi / 6.0 + #[inline(always)] + fn frac_pi_6() -> f64 { 0.52359877559829887307710723054658381 } + + /// pi / 8.0 + #[inline(always)] + fn frac_pi_8() -> f64 { 0.39269908169872415480783042290993786 } + + /// 1.0 / pi + #[inline(always)] + fn frac_1_pi() -> f64 { 0.318309886183790671537767526745028724 } + + /// 2.0 / pi + #[inline(always)] + fn frac_2_pi() -> f64 { 0.636619772367581343075535053490057448 } + + /// 2.0 / sqrt(pi) + #[inline(always)] + fn frac_2_sqrtpi() -> f64 { 1.12837916709551257389615890312154517 } + + /// sqrt(2.0) + #[inline(always)] + fn sqrt2() -> f64 { 1.41421356237309504880168872420969808 } + + /// 1.0 / sqrt(2.0) + #[inline(always)] + fn frac_1_sqrt2() -> f64 { 0.707106781186547524400844362104849039 } + + /// Euler's number + #[inline(always)] + fn e() -> f64 { 2.71828182845904523536028747135266250 } + + /// log2(e) + #[inline(always)] + fn log2_e() -> f64 { 1.44269504088896340735992468100189214 } + + /// log10(e) + #[inline(always)] + fn log10_e() -> f64 { 0.434294481903251827651128918916605082 } + + /// ln(2.0) + #[inline(always)] + fn ln_2() -> f64 { 0.693147180559945309417232121458176568 } + + /// ln(10.0) + #[inline(always)] + fn ln_10() -> f64 { 2.30258509299404568401799145468436421 } + + /// Converts to degrees, assuming the number is in radians + #[inline(always)] + fn to_degrees(&self) -> f64 { *self * (180.0 / Real::pi::<f64>()) } + + /// Converts to radians, assuming the number is in degrees + #[inline(always)] + fn to_radians(&self) -> f64 { *self * (Real::pi::<f64>() / 180.0) } +} + +impl RealExt for f64 { + #[inline(always)] + fn lgamma(&self) -> (int, f64) { + let mut sign = 0; + let result = lgamma(*self, &mut sign); + (sign as int, result) + } + + #[inline(always)] + fn tgamma(&self) -> f64 { tgamma(*self) } + + #[inline(always)] + fn j0(&self) -> f64 { j0(*self) } + + #[inline(always)] + fn j1(&self) -> f64 { j1(*self) } + + #[inline(always)] + fn jn(&self, n: int) -> f64 { jn(n as c_int, *self) } + + #[inline(always)] + fn y0(&self) -> f64 { y0(*self) } + + #[inline(always)] + fn y1(&self) -> f64 { y1(*self) } + + #[inline(always)] + fn yn(&self, n: int) -> f64 { yn(n as c_int, *self) } +} + +impl Bounded for f64 { + #[inline(always)] + fn min_value() -> f64 { 2.2250738585072014e-308 } + + #[inline(always)] + fn max_value() -> f64 { 1.7976931348623157e+308 } +} + +impl Primitive for f64 { + #[inline(always)] + fn bits() -> uint { 64 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<f64>() / 8 } +} + +impl Float for f64 { + #[inline(always)] + fn NaN() -> f64 { 0.0 / 0.0 } + + #[inline(always)] + fn infinity() -> f64 { 1.0 / 0.0 } + + #[inline(always)] + fn neg_infinity() -> f64 { -1.0 / 0.0 } + + #[inline(always)] + fn neg_zero() -> f64 { -0.0 } + + /// Returns `true` if the number is NaN + #[inline(always)] + fn is_NaN(&self) -> bool { *self != *self } + + /// Returns `true` if the number is infinite + #[inline(always)] + fn is_infinite(&self) -> bool { + *self == Float::infinity() || *self == Float::neg_infinity() + } + + /// Returns `true` if the number is neither infinite or NaN + #[inline(always)] + fn is_finite(&self) -> bool { + !(self.is_NaN() || self.is_infinite()) + } + + /// Returns `true` if the number is neither zero, infinite, subnormal or NaN + #[inline(always)] + fn is_normal(&self) -> bool { + self.classify() == FPNormal + } + + /// Returns the floating point category of the number. If only one property is going to + /// be tested, it is generally faster to use the specific predicate instead. + fn classify(&self) -> FPCategory { + static EXP_MASK: u64 = 0x7ff0000000000000; + static MAN_MASK: u64 = 0x000fffffffffffff; + + match ( + unsafe { ::cast::transmute::<f64,u64>(*self) } & MAN_MASK, + unsafe { ::cast::transmute::<f64,u64>(*self) } & EXP_MASK, + ) { + (0, 0) => FPZero, + (_, 0) => FPSubnormal, + (0, EXP_MASK) => FPInfinite, + (_, EXP_MASK) => FPNaN, + _ => FPNormal, + } + } + + #[inline(always)] + fn mantissa_digits() -> uint { 53 } + + #[inline(always)] + fn digits() -> uint { 15 } + + #[inline(always)] + fn epsilon() -> f64 { 2.2204460492503131e-16 } + + #[inline(always)] + fn min_exp() -> int { -1021 } + + #[inline(always)] + fn max_exp() -> int { 1024 } + + #[inline(always)] + fn min_10_exp() -> int { -307 } + + #[inline(always)] + fn max_10_exp() -> int { 308 } + + /// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp` + #[inline(always)] + fn ldexp(x: f64, exp: int) -> f64 { + ldexp(x, exp as c_int) + } + + /// + /// Breaks the number into a normalized fraction and a base-2 exponent, satisfying: + /// + /// - `self = x * pow(2, exp)` + /// - `0.5 <= abs(x) < 1.0` + /// + #[inline(always)] + fn frexp(&self) -> (f64, int) { + let mut exp = 0; + let x = frexp(*self, &mut exp); + (x, exp as int) + } + + /// + /// Returns the exponential of the number, minus `1`, in a way that is accurate + /// even if the number is close to zero + /// + #[inline(always)] + fn exp_m1(&self) -> f64 { exp_m1(*self) } + + /// + /// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately + /// than if the operations were performed separately + /// + #[inline(always)] + fn ln_1p(&self) -> f64 { ln_1p(*self) } + + /// + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This + /// produces a more accurate result with better performance than a separate multiplication + /// operation followed by an add. + /// + #[inline(always)] + fn mul_add(&self, a: f64, b: f64) -> f64 { + mul_add(*self, a, b) + } + + /// Returns the next representable floating-point value in the direction of `other` + #[inline(always)] + fn next_after(&self, other: f64) -> f64 { + next_after(*self, other) + } +} + +// +// Section: String Conversions +// + +/// +/// Converts a float to a string +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str(num: f64) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in hexadecimal format +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str_hex(num: f64) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in a given radix +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +/// # Failure +/// +/// Fails if called on a special value like `inf`, `-inf` or `NaN` due to +/// possible misinterpretation of the result at higher bases. If those values +/// are expected, use `to_str_radix_special()` instead. +/// +#[inline(always)] +pub fn to_str_radix(num: f64, rdx: uint) -> ~str { + let (r, special) = strconv::to_str_common( + &num, rdx, true, strconv::SignNeg, strconv::DigAll); + if special { fail!("number has a special value, \ + try to_str_radix_special() if those are expected") } + r +} + +/// +/// Converts a float to a string in a given radix, and a flag indicating +/// whether it's a special value +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +#[inline(always)] +pub fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { + strconv::to_str_common(&num, rdx, true, + strconv::SignNeg, strconv::DigAll) +} + +/// +/// Converts a float to a string with exactly the number of +/// provided significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_exact(num: f64, dig: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); + r +} + +/// +/// Converts a float to a string with a maximum number of +/// significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_digits(num: f64, dig: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); + r +} + +impl to_str::ToStr for f64 { + #[inline(always)] + fn to_str(&self) -> ~str { to_str_digits(*self, 8) } +} + +impl num::ToStrRadix for f64 { + #[inline(always)] + fn to_str_radix(&self, rdx: uint) -> ~str { + to_str_radix(*self, rdx) + } +} + +/// +/// Convert a string in base 10 to a float. +/// Accepts a optional decimal exponent. +/// +/// This function accepts strings such as +/// +/// * '3.14' +/// * '+3.14', equivalent to '3.14' +/// * '-3.14' +/// * '2.5E10', or equivalently, '2.5e10' +/// * '2.5E-10' +/// * '.' (understood as 0) +/// * '5.' +/// * '.5', or, equivalently, '0.5' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str(num: &str) -> Option<f64> { + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false, false) +} + +/// +/// Convert a string in base 16 to a float. +/// Accepts a optional binary exponent. +/// +/// This function accepts strings such as +/// +/// * 'a4.fe' +/// * '+a4.fe', equivalent to 'a4.fe' +/// * '-a4.fe' +/// * '2b.aP128', or equivalently, '2b.ap128' +/// * '2b.aP-128' +/// * '.' (understood as 0) +/// * 'c.' +/// * '.c', or, equivalently, '0.c' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `[num]`. +/// +#[inline(always)] +pub fn from_str_hex(num: &str) -> Option<f64> { + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false, false) +} + +/// +/// Convert a string in an given base to a float. +/// +/// Due to possible conflicts, this function does **not** accept +/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** +/// does it recognize exponents of any kind. +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// * radix - The base to use. Must lie in the range [2 .. 36] +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str_radix(num: &str, rdx: uint) -> Option<f64> { + strconv::from_str_common(num, rdx, true, true, false, + strconv::ExpNone, false, false) +} + +impl FromStr for f64 { + #[inline(always)] + fn from_str(val: &str) -> Option<f64> { from_str(val) } +} + +impl num::FromStrRadix for f64 { + #[inline(always)] + fn from_str_radix(val: &str, rdx: uint) -> Option<f64> { + from_str_radix(val, rdx) + } +} + +#[cfg(test)] +mod tests { + use f64::*; + use num::*; + use super::*; + use prelude::*; + + #[test] + fn test_num() { + num::test_num(10f64, 2f64); + } + + #[test] + fn test_min() { + assert_eq!(1f64.min(&2f64), 1f64); + assert_eq!(2f64.min(&1f64), 1f64); + assert!(1f64.min(&Float::NaN::<f64>()).is_NaN()); + assert!(Float::NaN::<f64>().min(&1f64).is_NaN()); + } + + #[test] + fn test_max() { + assert_eq!(1f64.max(&2f64), 2f64); + assert_eq!(2f64.max(&1f64), 2f64); + assert!(1f64.max(&Float::NaN::<f64>()).is_NaN()); + assert!(Float::NaN::<f64>().max(&1f64).is_NaN()); + } + + #[test] + fn test_clamp() { + assert_eq!(1f64.clamp(&2f64, &4f64), 2f64); + assert_eq!(8f64.clamp(&2f64, &4f64), 4f64); + assert_eq!(3f64.clamp(&2f64, &4f64), 3f64); + assert!(3f64.clamp(&Float::NaN::<f64>(), &4f64).is_NaN()); + assert!(3f64.clamp(&2f64, &Float::NaN::<f64>()).is_NaN()); + assert!(Float::NaN::<f64>().clamp(&2f64, &4f64).is_NaN()); + } + + #[test] + fn test_floor() { + assert_approx_eq!(1.0f64.floor(), 1.0f64); + assert_approx_eq!(1.3f64.floor(), 1.0f64); + assert_approx_eq!(1.5f64.floor(), 1.0f64); + assert_approx_eq!(1.7f64.floor(), 1.0f64); + assert_approx_eq!(0.0f64.floor(), 0.0f64); + assert_approx_eq!((-0.0f64).floor(), -0.0f64); + assert_approx_eq!((-1.0f64).floor(), -1.0f64); + assert_approx_eq!((-1.3f64).floor(), -2.0f64); + assert_approx_eq!((-1.5f64).floor(), -2.0f64); + assert_approx_eq!((-1.7f64).floor(), -2.0f64); + } + + #[test] + fn test_ceil() { + assert_approx_eq!(1.0f64.ceil(), 1.0f64); + assert_approx_eq!(1.3f64.ceil(), 2.0f64); + assert_approx_eq!(1.5f64.ceil(), 2.0f64); + assert_approx_eq!(1.7f64.ceil(), 2.0f64); + assert_approx_eq!(0.0f64.ceil(), 0.0f64); + assert_approx_eq!((-0.0f64).ceil(), -0.0f64); + assert_approx_eq!((-1.0f64).ceil(), -1.0f64); + assert_approx_eq!((-1.3f64).ceil(), -1.0f64); + assert_approx_eq!((-1.5f64).ceil(), -1.0f64); + assert_approx_eq!((-1.7f64).ceil(), -1.0f64); + } + + #[test] + fn test_round() { + assert_approx_eq!(1.0f64.round(), 1.0f64); + assert_approx_eq!(1.3f64.round(), 1.0f64); + assert_approx_eq!(1.5f64.round(), 2.0f64); + assert_approx_eq!(1.7f64.round(), 2.0f64); + assert_approx_eq!(0.0f64.round(), 0.0f64); + assert_approx_eq!((-0.0f64).round(), -0.0f64); + assert_approx_eq!((-1.0f64).round(), -1.0f64); + assert_approx_eq!((-1.3f64).round(), -1.0f64); + assert_approx_eq!((-1.5f64).round(), -2.0f64); + assert_approx_eq!((-1.7f64).round(), -2.0f64); + } + + #[test] + fn test_trunc() { + assert_approx_eq!(1.0f64.trunc(), 1.0f64); + assert_approx_eq!(1.3f64.trunc(), 1.0f64); + assert_approx_eq!(1.5f64.trunc(), 1.0f64); + assert_approx_eq!(1.7f64.trunc(), 1.0f64); + assert_approx_eq!(0.0f64.trunc(), 0.0f64); + assert_approx_eq!((-0.0f64).trunc(), -0.0f64); + assert_approx_eq!((-1.0f64).trunc(), -1.0f64); + assert_approx_eq!((-1.3f64).trunc(), -1.0f64); + assert_approx_eq!((-1.5f64).trunc(), -1.0f64); + assert_approx_eq!((-1.7f64).trunc(), -1.0f64); + } + + #[test] + fn test_fract() { + assert_approx_eq!(1.0f64.fract(), 0.0f64); + assert_approx_eq!(1.3f64.fract(), 0.3f64); + assert_approx_eq!(1.5f64.fract(), 0.5f64); + assert_approx_eq!(1.7f64.fract(), 0.7f64); + assert_approx_eq!(0.0f64.fract(), 0.0f64); + assert_approx_eq!((-0.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.3f64).fract(), -0.3f64); + assert_approx_eq!((-1.5f64).fract(), -0.5f64); + assert_approx_eq!((-1.7f64).fract(), -0.7f64); + } + + #[test] + fn test_asinh() { + assert_eq!(0.0f64.asinh(), 0.0f64); + assert_eq!((-0.0f64).asinh(), -0.0f64); + assert_eq!(Float::infinity::<f64>().asinh(), Float::infinity::<f64>()); + assert_eq!(Float::neg_infinity::<f64>().asinh(), Float::neg_infinity::<f64>()); + assert!(Float::NaN::<f64>().asinh().is_NaN()); + assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); + assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + } + + #[test] + fn test_acosh() { + assert_eq!(1.0f64.acosh(), 0.0f64); + assert!(0.999f64.acosh().is_NaN()); + assert_eq!(Float::infinity::<f64>().acosh(), Float::infinity::<f64>()); + assert!(Float::neg_infinity::<f64>().acosh().is_NaN()); + assert!(Float::NaN::<f64>().acosh().is_NaN()); + assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); + assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); + } + + #[test] + fn test_atanh() { + assert_eq!(0.0f64.atanh(), 0.0f64); + assert_eq!((-0.0f64).atanh(), -0.0f64); + assert_eq!(1.0f64.atanh(), Float::infinity::<f64>()); + assert_eq!((-1.0f64).atanh(), Float::neg_infinity::<f64>()); + assert!(2f64.atanh().atanh().is_NaN()); + assert!((-2f64).atanh().atanh().is_NaN()); + assert!(Float::infinity::<f64>().atanh().is_NaN()); + assert!(Float::neg_infinity::<f64>().atanh().is_NaN()); + assert!(Float::NaN::<f64>().atanh().is_NaN()); + assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); + assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); + } + + #[test] + fn test_real_consts() { + assert_approx_eq!(Real::two_pi::<f64>(), 2.0 * Real::pi::<f64>()); + assert_approx_eq!(Real::frac_pi_2::<f64>(), Real::pi::<f64>() / 2f64); + assert_approx_eq!(Real::frac_pi_3::<f64>(), Real::pi::<f64>() / 3f64); + assert_approx_eq!(Real::frac_pi_4::<f64>(), Real::pi::<f64>() / 4f64); + assert_approx_eq!(Real::frac_pi_6::<f64>(), Real::pi::<f64>() / 6f64); + assert_approx_eq!(Real::frac_pi_8::<f64>(), Real::pi::<f64>() / 8f64); + assert_approx_eq!(Real::frac_1_pi::<f64>(), 1f64 / Real::pi::<f64>()); + assert_approx_eq!(Real::frac_2_pi::<f64>(), 2f64 / Real::pi::<f64>()); + assert_approx_eq!(Real::frac_2_sqrtpi::<f64>(), 2f64 / Real::pi::<f64>().sqrt()); + assert_approx_eq!(Real::sqrt2::<f64>(), 2f64.sqrt()); + assert_approx_eq!(Real::frac_1_sqrt2::<f64>(), 1f64 / 2f64.sqrt()); + assert_approx_eq!(Real::log2_e::<f64>(), Real::e::<f64>().log2()); + assert_approx_eq!(Real::log10_e::<f64>(), Real::e::<f64>().log10()); + assert_approx_eq!(Real::ln_2::<f64>(), 2f64.ln()); + assert_approx_eq!(Real::ln_10::<f64>(), 10f64.ln()); + } + + #[test] + pub fn test_abs() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f64.abs(), 1f64); + assert_eq!(0f64.abs(), 0f64); + assert_eq!((-0f64).abs(), 0f64); + assert_eq!((-1f64).abs(), 1f64); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f64/neg_infinity).abs(), 0f64); + assert!(NaN.abs().is_NaN()); + } + + #[test] + fn test_abs_sub() { + assert_eq!((-1f64).abs_sub(&1f64), 0f64); + assert_eq!(1f64.abs_sub(&1f64), 0f64); + assert_eq!(1f64.abs_sub(&0f64), 1f64); + assert_eq!(1f64.abs_sub(&-1f64), 2f64); + assert_eq!(neg_infinity.abs_sub(&0f64), 0f64); + assert_eq!(infinity.abs_sub(&1f64), infinity); + assert_eq!(0f64.abs_sub(&neg_infinity), infinity); + assert_eq!(0f64.abs_sub(&infinity), 0f64); + assert!(NaN.abs_sub(&-1f64).is_NaN()); + assert!(1f64.abs_sub(&NaN).is_NaN()); + } + + #[test] + fn test_signum() { + assert_eq!(infinity.signum(), 1f64); + assert_eq!(1f64.signum(), 1f64); + assert_eq!(0f64.signum(), 1f64); + assert_eq!((-0f64).signum(), -1f64); + assert_eq!((-1f64).signum(), -1f64); + assert_eq!(neg_infinity.signum(), -1f64); + assert_eq!((1f64/neg_infinity).signum(), -1f64); + assert!(NaN.signum().is_NaN()); + } + + #[test] + fn test_is_positive() { + assert!(infinity.is_positive()); + assert!(1f64.is_positive()); + assert!(0f64.is_positive()); + assert!(!(-0f64).is_positive()); + assert!(!(-1f64).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f64/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + } + + #[test] + fn test_is_negative() { + assert!(!infinity.is_negative()); + assert!(!1f64.is_negative()); + assert!(!0f64.is_negative()); + assert!((-0f64).is_negative()); + assert!((-1f64).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f64/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } + + #[test] + fn test_approx_eq() { + assert!(1.0f64.approx_eq(&1f64)); + assert!(0.9999999f64.approx_eq(&1f64)); + assert!(1.000001f64.approx_eq_eps(&1f64, &1.0e-5)); + assert!(1.0000001f64.approx_eq_eps(&1f64, &1.0e-6)); + assert!(!1.0000001f64.approx_eq_eps(&1f64, &1.0e-7)); + } + + #[test] + fn test_primitive() { + assert_eq!(Primitive::bits::<f64>(), sys::size_of::<f64>() * 8); + assert_eq!(Primitive::bytes::<f64>(), sys::size_of::<f64>()); + } + + #[test] + fn test_is_normal() { + assert!(!Float::NaN::<f64>().is_normal()); + assert!(!Float::infinity::<f64>().is_normal()); + assert!(!Float::neg_infinity::<f64>().is_normal()); + assert!(!Zero::zero::<f64>().is_normal()); + assert!(!Float::neg_zero::<f64>().is_normal()); + assert!(1f64.is_normal()); + assert!(1e-307f64.is_normal()); + assert!(!1e-308f64.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::<f64>().classify(), FPNaN); + assert_eq!(Float::infinity::<f64>().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::<f64>().classify(), FPInfinite); + assert_eq!(Zero::zero::<f64>().classify(), FPZero); + assert_eq!(Float::neg_zero::<f64>().classify(), FPZero); + assert_eq!(1e-307f64.classify(), FPNormal); + assert_eq!(1e-308f64.classify(), FPSubnormal); + } + + #[test] + fn test_ldexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: f64 = from_str_hex("1p-123").unwrap(); + let f2: f64 = from_str_hex("1p-111").unwrap(); + assert_eq!(Float::ldexp(1f64, -123), f1); + assert_eq!(Float::ldexp(1f64, -111), f2); + + assert_eq!(Float::ldexp(0f64, -123), 0f64); + assert_eq!(Float::ldexp(-0f64, -123), -0f64); + assert_eq!(Float::ldexp(Float::infinity::<f64>(), -123), + Float::infinity::<f64>()); + assert_eq!(Float::ldexp(Float::neg_infinity::<f64>(), -123), + Float::neg_infinity::<f64>()); + assert!(Float::ldexp(Float::NaN::<f64>(), -123).is_NaN()); + } + + #[test] + fn test_frexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: f64 = from_str_hex("1p-123").unwrap(); + let f2: f64 = from_str_hex("1p-111").unwrap(); + let (x1, exp1) = f1.frexp(); + let (x2, exp2) = f2.frexp(); + assert_eq!((x1, exp1), (0.5f64, -122)); + assert_eq!((x2, exp2), (0.5f64, -110)); + assert_eq!(Float::ldexp(x1, exp1), f1); + assert_eq!(Float::ldexp(x2, exp2), f2); + + assert_eq!(0f64.frexp(), (0f64, 0)); + assert_eq!((-0f64).frexp(), (-0f64, 0)); + assert_eq!(match Float::infinity::<f64>().frexp() { (x, _) => x }, + Float::infinity::<f64>()) + assert_eq!(match Float::neg_infinity::<f64>().frexp() { (x, _) => x }, + Float::neg_infinity::<f64>()) + assert!(match Float::NaN::<f64>().frexp() { (x, _) => x.is_NaN() }) + } +} diff --git a/src/libstd/num/float.rs b/src/libstd/num/float.rs new file mode 100644 index 00000000000..681aafaab88 --- /dev/null +++ b/src/libstd/num/float.rs @@ -0,0 +1,1387 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `float` + +// Even though this module exports everything defined in it, +// because it contains re-exports, we also have to explicitly +// export locally defined things. That's a bit annoying. + + +// export when m_float == c_double + + +// PORT this must match in width according to architecture + +use libc::c_int; +use num::{Zero, One, strconv}; +use num::FPCategory; +use prelude::*; + +pub use f64::{add, sub, mul, div, rem, lt, le, eq, ne, ge, gt}; +pub use f64::{acos, asin, atan2, cbrt, ceil, copysign, cosh, floor}; +pub use f64::{erf, erfc, exp, exp_m1, exp2, abs_sub}; +pub use f64::{mul_add, fmax, fmin, next_after, frexp, hypot, ldexp}; +pub use f64::{lgamma, ln, log_radix, ln_1p, log10, log2, ilog_radix}; +pub use f64::{modf, pow, powi, round, sinh, tanh, tgamma, trunc}; +pub use f64::{j0, j1, jn, y0, y1, yn}; + +pub static NaN: float = 0.0/0.0; + +pub static infinity: float = 1.0/0.0; + +pub static neg_infinity: float = -1.0/0.0; + +/* Module: consts */ +pub mod consts { + // FIXME (requires Issue #1433 to fix): replace with mathematical + // staticants from cmath. + /// Archimedes' staticant + pub static pi: float = 3.14159265358979323846264338327950288; + + /// pi/2.0 + pub static frac_pi_2: float = 1.57079632679489661923132169163975144; + + /// pi/4.0 + pub static frac_pi_4: float = 0.785398163397448309615660845819875721; + + /// 1.0/pi + pub static frac_1_pi: float = 0.318309886183790671537767526745028724; + + /// 2.0/pi + pub static frac_2_pi: float = 0.636619772367581343075535053490057448; + + /// 2.0/sqrt(pi) + pub static frac_2_sqrtpi: float = 1.12837916709551257389615890312154517; + + /// sqrt(2.0) + pub static sqrt2: float = 1.41421356237309504880168872420969808; + + /// 1.0/sqrt(2.0) + pub static frac_1_sqrt2: float = 0.707106781186547524400844362104849039; + + /// Euler's number + pub static e: float = 2.71828182845904523536028747135266250; + + /// log2(e) + pub static log2_e: float = 1.44269504088896340735992468100189214; + + /// log10(e) + pub static log10_e: float = 0.434294481903251827651128918916605082; + + /// ln(2.0) + pub static ln_2: float = 0.693147180559945309417232121458176568; + + /// ln(10.0) + pub static ln_10: float = 2.30258509299404568401799145468436421; +} + +// +// Section: String Conversions +// + +/// +/// Converts a float to a string +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str(num: float) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in hexadecimal format +/// +/// # Arguments +/// +/// * num - The float value +/// +#[inline(always)] +pub fn to_str_hex(num: float) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); + r +} + +/// +/// Converts a float to a string in a given radix +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +/// # Failure +/// +/// Fails if called on a special value like `inf`, `-inf` or `NaN` due to +/// possible misinterpretation of the result at higher bases. If those values +/// are expected, use `to_str_radix_special()` instead. +/// +#[inline(always)] +pub fn to_str_radix(num: float, radix: uint) -> ~str { + let (r, special) = strconv::to_str_common( + &num, radix, true, strconv::SignNeg, strconv::DigAll); + if special { fail!("number has a special value, \ + try to_str_radix_special() if those are expected") } + r +} + +/// +/// Converts a float to a string in a given radix, and a flag indicating +/// whether it's a special value +/// +/// # Arguments +/// +/// * num - The float value +/// * radix - The base to use +/// +#[inline(always)] +pub fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { + strconv::to_str_common(&num, radix, true, + strconv::SignNeg, strconv::DigAll) +} + +/// +/// Converts a float to a string with exactly the number of +/// provided significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_exact(num: float, digits: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(digits)); + r +} + +/// +/// Converts a float to a string with a maximum number of +/// significant digits +/// +/// # Arguments +/// +/// * num - The float value +/// * digits - The number of significant digits +/// +#[inline(always)] +pub fn to_str_digits(num: float, digits: uint) -> ~str { + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(digits)); + r +} + +impl to_str::ToStr for float { + #[inline(always)] + fn to_str(&self) -> ~str { to_str_digits(*self, 8) } +} + +impl num::ToStrRadix for float { + #[inline(always)] + fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } +} + +/// +/// Convert a string in base 10 to a float. +/// Accepts a optional decimal exponent. +/// +/// This function accepts strings such as +/// +/// * '3.14' +/// * '+3.14', equivalent to '3.14' +/// * '-3.14' +/// * '2.5E10', or equivalently, '2.5e10' +/// * '2.5E-10' +/// * '.' (understood as 0) +/// * '5.' +/// * '.5', or, equivalently, '0.5' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str(num: &str) -> Option<float> { + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false, false) +} + +/// +/// Convert a string in base 16 to a float. +/// Accepts a optional binary exponent. +/// +/// This function accepts strings such as +/// +/// * 'a4.fe' +/// * '+a4.fe', equivalent to 'a4.fe' +/// * '-a4.fe' +/// * '2b.aP128', or equivalently, '2b.ap128' +/// * '2b.aP-128' +/// * '.' (understood as 0) +/// * 'c.' +/// * '.c', or, equivalently, '0.c' +/// * '+inf', 'inf', '-inf', 'NaN' +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `[num]`. +/// +#[inline(always)] +pub fn from_str_hex(num: &str) -> Option<float> { + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false, false) +} + +/// +/// Convert a string in an given base to a float. +/// +/// Due to possible conflicts, this function does **not** accept +/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** +/// does it recognize exponents of any kind. +/// +/// Leading and trailing whitespace represent an error. +/// +/// # Arguments +/// +/// * num - A string +/// * radix - The base to use. Must lie in the range [2 .. 36] +/// +/// # Return value +/// +/// `none` if the string did not represent a valid number. Otherwise, +/// `Some(n)` where `n` is the floating-point number represented by `num`. +/// +#[inline(always)] +pub fn from_str_radix(num: &str, radix: uint) -> Option<float> { + strconv::from_str_common(num, radix, true, true, false, + strconv::ExpNone, false, false) +} + +impl FromStr for float { + #[inline(always)] + fn from_str(val: &str) -> Option<float> { from_str(val) } +} + +impl num::FromStrRadix for float { + #[inline(always)] + fn from_str_radix(val: &str, radix: uint) -> Option<float> { + from_str_radix(val, radix) + } +} + +// +// Section: Arithmetics +// + +/// +/// Compute the exponentiation of an integer by another integer as a float +/// +/// # Arguments +/// +/// * x - The base +/// * pow - The exponent +/// +/// # Return value +/// +/// `NaN` if both `x` and `pow` are `0u`, otherwise `x^pow` +/// +pub fn pow_with_uint(base: uint, pow: uint) -> float { + if base == 0u { + if pow == 0u { + return NaN as float; + } + return 0.; + } + let mut my_pow = pow; + let mut total = 1f; + let mut multiplier = base as float; + while (my_pow > 0u) { + if my_pow % 2u == 1u { + total = total * multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + return total; +} + +#[inline(always)] +pub fn abs(x: float) -> float { + f64::abs(x as f64) as float +} +#[inline(always)] +pub fn sqrt(x: float) -> float { + f64::sqrt(x as f64) as float +} +#[inline(always)] +pub fn atan(x: float) -> float { + f64::atan(x as f64) as float +} +#[inline(always)] +pub fn sin(x: float) -> float { + f64::sin(x as f64) as float +} +#[inline(always)] +pub fn cos(x: float) -> float { + f64::cos(x as f64) as float +} +#[inline(always)] +pub fn tan(x: float) -> float { + f64::tan(x as f64) as float +} + +impl Num for float {} + +#[cfg(not(test))] +impl Eq for float { + #[inline(always)] + fn eq(&self, other: &float) -> bool { (*self) == (*other) } + #[inline(always)] + fn ne(&self, other: &float) -> bool { (*self) != (*other) } +} + +#[cfg(not(test))] +impl ApproxEq<float> for float { + #[inline(always)] + fn approx_epsilon() -> float { 1.0e-6 } + + #[inline(always)] + fn approx_eq(&self, other: &float) -> bool { + self.approx_eq_eps(other, &ApproxEq::approx_epsilon::<float, float>()) + } + + #[inline(always)] + fn approx_eq_eps(&self, other: &float, approx_epsilon: &float) -> bool { + (*self - *other).abs() < *approx_epsilon + } +} + +#[cfg(not(test))] +impl Ord for float { + #[inline(always)] + fn lt(&self, other: &float) -> bool { (*self) < (*other) } + #[inline(always)] + fn le(&self, other: &float) -> bool { (*self) <= (*other) } + #[inline(always)] + fn ge(&self, other: &float) -> bool { (*self) >= (*other) } + #[inline(always)] + fn gt(&self, other: &float) -> bool { (*self) > (*other) } +} + +impl Orderable for float { + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn min(&self, other: &float) -> float { + (*self as f64).min(&(*other as f64)) as float + } + + /// Returns `NaN` if either of the numbers are `NaN`. + #[inline(always)] + fn max(&self, other: &float) -> float { + (*self as f64).max(&(*other as f64)) as float + } + + /// Returns the number constrained within the range `mn <= self <= mx`. + /// If any of the numbers are `NaN` then `NaN` is returned. + #[inline(always)] + fn clamp(&self, mn: &float, mx: &float) -> float { + (*self as f64).clamp(&(*mn as f64), &(*mx as f64)) as float + } +} + +impl Zero for float { + #[inline(always)] + fn zero() -> float { 0.0 } + + /// Returns true if the number is equal to either `0.0` or `-0.0` + #[inline(always)] + fn is_zero(&self) -> bool { *self == 0.0 || *self == -0.0 } +} + +impl One for float { + #[inline(always)] + fn one() -> float { 1.0 } +} + +impl Round for float { + /// Round half-way cases toward `neg_infinity` + #[inline(always)] + fn floor(&self) -> float { floor(*self as f64) as float } + + /// Round half-way cases toward `infinity` + #[inline(always)] + fn ceil(&self) -> float { ceil(*self as f64) as float } + + /// Round half-way cases away from `0.0` + #[inline(always)] + fn round(&self) -> float { round(*self as f64) as float } + + /// The integer part of the number (rounds towards `0.0`) + #[inline(always)] + fn trunc(&self) -> float { trunc(*self as f64) as float } + + /// + /// The fractional part of the number, satisfying: + /// + /// ~~~ + /// assert!(x == trunc(x) + fract(x)) + /// ~~~ + /// + #[inline(always)] + fn fract(&self) -> float { *self - self.trunc() } +} + +impl Fractional for float { + /// The reciprocal (multiplicative inverse) of the number + #[inline(always)] + fn recip(&self) -> float { 1.0 / *self } +} + +impl Algebraic for float { + #[inline(always)] + fn pow(&self, n: float) -> float { + (*self as f64).pow(n as f64) as float + } + + #[inline(always)] + fn sqrt(&self) -> float { + (*self as f64).sqrt() as float + } + + #[inline(always)] + fn rsqrt(&self) -> float { + (*self as f64).rsqrt() as float + } + + #[inline(always)] + fn cbrt(&self) -> float { + (*self as f64).cbrt() as float + } + + #[inline(always)] + fn hypot(&self, other: float) -> float { + (*self as f64).hypot(other as f64) as float + } +} + +impl Trigonometric for float { + #[inline(always)] + fn sin(&self) -> float { + (*self as f64).sin() as float + } + + #[inline(always)] + fn cos(&self) -> float { + (*self as f64).cos() as float + } + + #[inline(always)] + fn tan(&self) -> float { + (*self as f64).tan() as float + } + + #[inline(always)] + fn asin(&self) -> float { + (*self as f64).asin() as float + } + + #[inline(always)] + fn acos(&self) -> float { + (*self as f64).acos() as float + } + + #[inline(always)] + fn atan(&self) -> float { + (*self as f64).atan() as float + } + + #[inline(always)] + fn atan2(&self, other: float) -> float { + (*self as f64).atan2(other as f64) as float + } + + /// Simultaneously computes the sine and cosine of the number + #[inline(always)] + fn sin_cos(&self) -> (float, float) { + match (*self as f64).sin_cos() { + (s, c) => (s as float, c as float) + } + } +} + +impl Exponential for float { + /// Returns the exponential of the number + #[inline(always)] + fn exp(&self) -> float { + (*self as f64).exp() as float + } + + /// Returns 2 raised to the power of the number + #[inline(always)] + fn exp2(&self) -> float { + (*self as f64).exp2() as float + } + + /// Returns the natural logarithm of the number + #[inline(always)] + fn ln(&self) -> float { + (*self as f64).ln() as float + } + + /// Returns the logarithm of the number with respect to an arbitrary base + #[inline(always)] + fn log(&self, base: float) -> float { + (*self as f64).log(base as f64) as float + } + + /// Returns the base 2 logarithm of the number + #[inline(always)] + fn log2(&self) -> float { + (*self as f64).log2() as float + } + + /// Returns the base 10 logarithm of the number + #[inline(always)] + fn log10(&self) -> float { + (*self as f64).log10() as float + } +} + +impl Hyperbolic for float { + #[inline(always)] + fn sinh(&self) -> float { + (*self as f64).sinh() as float + } + + #[inline(always)] + fn cosh(&self) -> float { + (*self as f64).cosh() as float + } + + #[inline(always)] + fn tanh(&self) -> float { + (*self as f64).tanh() as float + } + + /// + /// Inverse hyperbolic sine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic sine of `self` will be returned + /// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity` + /// - `NaN` if `self` is `NaN` + /// + #[inline(always)] + fn asinh(&self) -> float { + (*self as f64).asinh() as float + } + + /// + /// Inverse hyperbolic cosine + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic cosine of `self` will be returned + /// - `infinity` if `self` is `infinity` + /// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`) + /// + #[inline(always)] + fn acosh(&self) -> float { + (*self as f64).acosh() as float + } + + /// + /// Inverse hyperbolic tangent + /// + /// # Returns + /// + /// - on success, the inverse hyperbolic tangent of `self` will be returned + /// - `self` if `self` is `0.0` or `-0.0` + /// - `infinity` if `self` is `1.0` + /// - `neg_infinity` if `self` is `-1.0` + /// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0` + /// (including `infinity` and `neg_infinity`) + /// + #[inline(always)] + fn atanh(&self) -> float { + (*self as f64).atanh() as float + } +} + +impl Real for float { + /// Archimedes' constant + #[inline(always)] + fn pi() -> float { 3.14159265358979323846264338327950288 } + + /// 2.0 * pi + #[inline(always)] + fn two_pi() -> float { 6.28318530717958647692528676655900576 } + + /// pi / 2.0 + #[inline(always)] + fn frac_pi_2() -> float { 1.57079632679489661923132169163975144 } + + /// pi / 3.0 + #[inline(always)] + fn frac_pi_3() -> float { 1.04719755119659774615421446109316763 } + + /// pi / 4.0 + #[inline(always)] + fn frac_pi_4() -> float { 0.785398163397448309615660845819875721 } + + /// pi / 6.0 + #[inline(always)] + fn frac_pi_6() -> float { 0.52359877559829887307710723054658381 } + + /// pi / 8.0 + #[inline(always)] + fn frac_pi_8() -> float { 0.39269908169872415480783042290993786 } + + /// 1.0 / pi + #[inline(always)] + fn frac_1_pi() -> float { 0.318309886183790671537767526745028724 } + + /// 2.0 / pi + #[inline(always)] + fn frac_2_pi() -> float { 0.636619772367581343075535053490057448 } + + /// 2 .0/ sqrt(pi) + #[inline(always)] + fn frac_2_sqrtpi() -> float { 1.12837916709551257389615890312154517 } + + /// sqrt(2.0) + #[inline(always)] + fn sqrt2() -> float { 1.41421356237309504880168872420969808 } + + /// 1.0 / sqrt(2.0) + #[inline(always)] + fn frac_1_sqrt2() -> float { 0.707106781186547524400844362104849039 } + + /// Euler's number + #[inline(always)] + fn e() -> float { 2.71828182845904523536028747135266250 } + + /// log2(e) + #[inline(always)] + fn log2_e() -> float { 1.44269504088896340735992468100189214 } + + /// log10(e) + #[inline(always)] + fn log10_e() -> float { 0.434294481903251827651128918916605082 } + + /// ln(2.0) + #[inline(always)] + fn ln_2() -> float { 0.693147180559945309417232121458176568 } + + /// ln(10.0) + #[inline(always)] + fn ln_10() -> float { 2.30258509299404568401799145468436421 } + + /// Converts to degrees, assuming the number is in radians + #[inline(always)] + fn to_degrees(&self) -> float { (*self as f64).to_degrees() as float } + + /// Converts to radians, assuming the number is in degrees + #[inline(always)] + fn to_radians(&self) -> float { (*self as f64).to_radians() as float } +} + +impl RealExt for float { + #[inline(always)] + fn lgamma(&self) -> (int, float) { + let mut sign = 0; + let result = lgamma(*self as f64, &mut sign); + (sign as int, result as float) + } + + #[inline(always)] + fn tgamma(&self) -> float { tgamma(*self as f64) as float } + + #[inline(always)] + fn j0(&self) -> float { j0(*self as f64) as float } + + #[inline(always)] + fn j1(&self) -> float { j1(*self as f64) as float } + + #[inline(always)] + fn jn(&self, n: int) -> float { jn(n as c_int, *self as f64) as float } + + #[inline(always)] + fn y0(&self) -> float { y0(*self as f64) as float } + + #[inline(always)] + fn y1(&self) -> float { y1(*self as f64) as float } + + #[inline(always)] + fn yn(&self, n: int) -> float { yn(n as c_int, *self as f64) as float } +} + +#[cfg(not(test))] +impl Add<float,float> for float { + #[inline(always)] + fn add(&self, other: &float) -> float { *self + *other } +} + +#[cfg(not(test))] +impl Sub<float,float> for float { + #[inline(always)] + fn sub(&self, other: &float) -> float { *self - *other } +} + +#[cfg(not(test))] +impl Mul<float,float> for float { + #[inline(always)] + fn mul(&self, other: &float) -> float { *self * *other } +} + +#[cfg(not(test))] +impl Div<float,float> for float { + #[inline(always)] + fn div(&self, other: &float) -> float { *self / *other } +} + +#[cfg(not(test))] +impl Rem<float,float> for float { + #[inline(always)] + fn rem(&self, other: &float) -> float { *self % *other } +} +#[cfg(not(test))] +impl Neg<float> for float { + #[inline(always)] + fn neg(&self) -> float { -*self } +} + +impl Signed for float { + /// Computes the absolute value. Returns `NaN` if the number is `NaN`. + #[inline(always)] + fn abs(&self) -> float { abs(*self) } + + /// + /// The positive difference of two numbers. Returns `0.0` if the number is less than or + /// equal to `other`, otherwise the difference between`self` and `other` is returned. + /// + #[inline(always)] + fn abs_sub(&self, other: &float) -> float { + (*self as f64).abs_sub(&(*other as f64)) as float + } + + /// + /// # Returns + /// + /// - `1.0` if the number is positive, `+0.0` or `infinity` + /// - `-1.0` if the number is negative, `-0.0` or `neg_infinity` + /// - `NaN` if the number is NaN + /// + #[inline(always)] + fn signum(&self) -> float { + if self.is_NaN() { NaN } else { f64::copysign(1.0, *self as f64) as float } + } + + /// Returns `true` if the number is positive, including `+0.0` and `infinity` + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == infinity } + + /// Returns `true` if the number is negative, including `-0.0` and `neg_infinity` + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == neg_infinity } +} + +impl Bounded for float { + #[inline(always)] + fn min_value() -> float { Bounded::min_value::<f64>() as float } + + #[inline(always)] + fn max_value() -> float { Bounded::max_value::<f64>() as float } +} + +impl Primitive for float { + #[inline(always)] + fn bits() -> uint { Primitive::bits::<f64>() } + + #[inline(always)] + fn bytes() -> uint { Primitive::bytes::<f64>() } +} + +impl Float for float { + #[inline(always)] + fn NaN() -> float { Float::NaN::<f64>() as float } + + #[inline(always)] + fn infinity() -> float { Float::infinity::<f64>() as float } + + #[inline(always)] + fn neg_infinity() -> float { Float::neg_infinity::<f64>() as float } + + #[inline(always)] + fn neg_zero() -> float { Float::neg_zero::<f64>() as float } + + /// Returns `true` if the number is NaN + #[inline(always)] + fn is_NaN(&self) -> bool { (*self as f64).is_NaN() } + + /// Returns `true` if the number is infinite + #[inline(always)] + fn is_infinite(&self) -> bool { (*self as f64).is_infinite() } + + /// Returns `true` if the number is neither infinite or NaN + #[inline(always)] + fn is_finite(&self) -> bool { (*self as f64).is_finite() } + + /// Returns `true` if the number is neither zero, infinite, subnormal or NaN + #[inline(always)] + fn is_normal(&self) -> bool { (*self as f64).is_normal() } + + /// Returns the floating point category of the number. If only one property is going to + /// be tested, it is generally faster to use the specific predicate instead. + #[inline(always)] + fn classify(&self) -> FPCategory { (*self as f64).classify() } + + #[inline(always)] + fn mantissa_digits() -> uint { Float::mantissa_digits::<f64>() } + + #[inline(always)] + fn digits() -> uint { Float::digits::<f64>() } + + #[inline(always)] + fn epsilon() -> float { Float::epsilon::<f64>() as float } + + #[inline(always)] + fn min_exp() -> int { Float::min_exp::<f64>() } + + #[inline(always)] + fn max_exp() -> int { Float::max_exp::<f64>() } + + #[inline(always)] + fn min_10_exp() -> int { Float::min_10_exp::<f64>() } + + #[inline(always)] + fn max_10_exp() -> int { Float::max_10_exp::<f64>() } + + /// Constructs a floating point number by multiplying `x` by 2 raised to the power of `exp` + #[inline(always)] + fn ldexp(x: float, exp: int) -> float { + Float::ldexp(x as f64, exp) as float + } + + /// + /// Breaks the number into a normalized fraction and a base-2 exponent, satisfying: + /// + /// - `self = x * pow(2, exp)` + /// - `0.5 <= abs(x) < 1.0` + /// + #[inline(always)] + fn frexp(&self) -> (float, int) { + match (*self as f64).frexp() { + (x, exp) => (x as float, exp) + } + } + + /// + /// Returns the exponential of the number, minus `1`, in a way that is accurate + /// even if the number is close to zero + /// + #[inline(always)] + fn exp_m1(&self) -> float { + (*self as f64).exp_m1() as float + } + + /// + /// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately + /// than if the operations were performed separately + /// + #[inline(always)] + fn ln_1p(&self) -> float { + (*self as f64).ln_1p() as float + } + + /// + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This + /// produces a more accurate result with better performance than a separate multiplication + /// operation followed by an add. + /// + #[inline(always)] + fn mul_add(&self, a: float, b: float) -> float { + mul_add(*self as f64, a as f64, b as f64) as float + } + + /// Returns the next representable floating-point value in the direction of `other` + #[inline(always)] + fn next_after(&self, other: float) -> float { + next_after(*self as f64, other as f64) as float + } +} + +#[cfg(test)] +mod tests { + use num::*; + use super::*; + use prelude::*; + + #[test] + fn test_num() { + num::test_num(10f, 2f); + } + + #[test] + fn test_min() { + assert_eq!(1f.min(&2f), 1f); + assert_eq!(2f.min(&1f), 1f); + } + + #[test] + fn test_max() { + assert_eq!(1f.max(&2f), 2f); + assert_eq!(2f.max(&1f), 2f); + } + + #[test] + fn test_clamp() { + assert_eq!(1f.clamp(&2f, &4f), 2f); + assert_eq!(8f.clamp(&2f, &4f), 4f); + assert_eq!(3f.clamp(&2f, &4f), 3f); + assert!(3f.clamp(&Float::NaN::<float>(), &4f).is_NaN()); + assert!(3f.clamp(&2f, &Float::NaN::<float>()).is_NaN()); + assert!(Float::NaN::<float>().clamp(&2f, &4f).is_NaN()); + } + + #[test] + fn test_floor() { + assert_approx_eq!(1.0f.floor(), 1.0f); + assert_approx_eq!(1.3f.floor(), 1.0f); + assert_approx_eq!(1.5f.floor(), 1.0f); + assert_approx_eq!(1.7f.floor(), 1.0f); + assert_approx_eq!(0.0f.floor(), 0.0f); + assert_approx_eq!((-0.0f).floor(), -0.0f); + assert_approx_eq!((-1.0f).floor(), -1.0f); + assert_approx_eq!((-1.3f).floor(), -2.0f); + assert_approx_eq!((-1.5f).floor(), -2.0f); + assert_approx_eq!((-1.7f).floor(), -2.0f); + } + + #[test] + fn test_ceil() { + assert_approx_eq!(1.0f.ceil(), 1.0f); + assert_approx_eq!(1.3f.ceil(), 2.0f); + assert_approx_eq!(1.5f.ceil(), 2.0f); + assert_approx_eq!(1.7f.ceil(), 2.0f); + assert_approx_eq!(0.0f.ceil(), 0.0f); + assert_approx_eq!((-0.0f).ceil(), -0.0f); + assert_approx_eq!((-1.0f).ceil(), -1.0f); + assert_approx_eq!((-1.3f).ceil(), -1.0f); + assert_approx_eq!((-1.5f).ceil(), -1.0f); + assert_approx_eq!((-1.7f).ceil(), -1.0f); + } + + #[test] + fn test_round() { + assert_approx_eq!(1.0f.round(), 1.0f); + assert_approx_eq!(1.3f.round(), 1.0f); + assert_approx_eq!(1.5f.round(), 2.0f); + assert_approx_eq!(1.7f.round(), 2.0f); + assert_approx_eq!(0.0f.round(), 0.0f); + assert_approx_eq!((-0.0f).round(), -0.0f); + assert_approx_eq!((-1.0f).round(), -1.0f); + assert_approx_eq!((-1.3f).round(), -1.0f); + assert_approx_eq!((-1.5f).round(), -2.0f); + assert_approx_eq!((-1.7f).round(), -2.0f); + } + + #[test] + fn test_trunc() { + assert_approx_eq!(1.0f.trunc(), 1.0f); + assert_approx_eq!(1.3f.trunc(), 1.0f); + assert_approx_eq!(1.5f.trunc(), 1.0f); + assert_approx_eq!(1.7f.trunc(), 1.0f); + assert_approx_eq!(0.0f.trunc(), 0.0f); + assert_approx_eq!((-0.0f).trunc(), -0.0f); + assert_approx_eq!((-1.0f).trunc(), -1.0f); + assert_approx_eq!((-1.3f).trunc(), -1.0f); + assert_approx_eq!((-1.5f).trunc(), -1.0f); + assert_approx_eq!((-1.7f).trunc(), -1.0f); + } + + #[test] + fn test_fract() { + assert_approx_eq!(1.0f.fract(), 0.0f); + assert_approx_eq!(1.3f.fract(), 0.3f); + assert_approx_eq!(1.5f.fract(), 0.5f); + assert_approx_eq!(1.7f.fract(), 0.7f); + assert_approx_eq!(0.0f.fract(), 0.0f); + assert_approx_eq!((-0.0f).fract(), -0.0f); + assert_approx_eq!((-1.0f).fract(), -0.0f); + assert_approx_eq!((-1.3f).fract(), -0.3f); + assert_approx_eq!((-1.5f).fract(), -0.5f); + assert_approx_eq!((-1.7f).fract(), -0.7f); + } + + #[test] + fn test_asinh() { + assert_eq!(0.0f.asinh(), 0.0f); + assert_eq!((-0.0f).asinh(), -0.0f); + assert_eq!(Float::infinity::<float>().asinh(), Float::infinity::<float>()); + assert_eq!(Float::neg_infinity::<float>().asinh(), Float::neg_infinity::<float>()); + assert!(Float::NaN::<float>().asinh().is_NaN()); + assert_approx_eq!(2.0f.asinh(), 1.443635475178810342493276740273105f); + assert_approx_eq!((-2.0f).asinh(), -1.443635475178810342493276740273105f); + } + + #[test] + fn test_acosh() { + assert_eq!(1.0f.acosh(), 0.0f); + assert!(0.999f.acosh().is_NaN()); + assert_eq!(Float::infinity::<float>().acosh(), Float::infinity::<float>()); + assert!(Float::neg_infinity::<float>().acosh().is_NaN()); + assert!(Float::NaN::<float>().acosh().is_NaN()); + assert_approx_eq!(2.0f.acosh(), 1.31695789692481670862504634730796844f); + assert_approx_eq!(3.0f.acosh(), 1.76274717403908605046521864995958461f); + } + + #[test] + fn test_atanh() { + assert_eq!(0.0f.atanh(), 0.0f); + assert_eq!((-0.0f).atanh(), -0.0f); + assert_eq!(1.0f.atanh(), Float::infinity::<float>()); + assert_eq!((-1.0f).atanh(), Float::neg_infinity::<float>()); + assert!(2f64.atanh().atanh().is_NaN()); + assert!((-2f64).atanh().atanh().is_NaN()); + assert!(Float::infinity::<f64>().atanh().is_NaN()); + assert!(Float::neg_infinity::<f64>().atanh().is_NaN()); + assert!(Float::NaN::<float>().atanh().is_NaN()); + assert_approx_eq!(0.5f.atanh(), 0.54930614433405484569762261846126285f); + assert_approx_eq!((-0.5f).atanh(), -0.54930614433405484569762261846126285f); + } + + #[test] + fn test_real_consts() { + assert_approx_eq!(Real::two_pi::<float>(), 2f * Real::pi::<float>()); + assert_approx_eq!(Real::frac_pi_2::<float>(), Real::pi::<float>() / 2f); + assert_approx_eq!(Real::frac_pi_3::<float>(), Real::pi::<float>() / 3f); + assert_approx_eq!(Real::frac_pi_4::<float>(), Real::pi::<float>() / 4f); + assert_approx_eq!(Real::frac_pi_6::<float>(), Real::pi::<float>() / 6f); + assert_approx_eq!(Real::frac_pi_8::<float>(), Real::pi::<float>() / 8f); + assert_approx_eq!(Real::frac_1_pi::<float>(), 1f / Real::pi::<float>()); + assert_approx_eq!(Real::frac_2_pi::<float>(), 2f / Real::pi::<float>()); + assert_approx_eq!(Real::frac_2_sqrtpi::<float>(), 2f / Real::pi::<float>().sqrt()); + assert_approx_eq!(Real::sqrt2::<float>(), 2f.sqrt()); + assert_approx_eq!(Real::frac_1_sqrt2::<float>(), 1f / 2f.sqrt()); + assert_approx_eq!(Real::log2_e::<float>(), Real::e::<float>().log2()); + assert_approx_eq!(Real::log10_e::<float>(), Real::e::<float>().log10()); + assert_approx_eq!(Real::ln_2::<float>(), 2f.ln()); + assert_approx_eq!(Real::ln_10::<float>(), 10f.ln()); + } + + #[test] + fn test_abs() { + assert_eq!(infinity.abs(), infinity); + assert_eq!(1f.abs(), 1f); + assert_eq!(0f.abs(), 0f); + assert_eq!((-0f).abs(), 0f); + assert_eq!((-1f).abs(), 1f); + assert_eq!(neg_infinity.abs(), infinity); + assert_eq!((1f/neg_infinity).abs(), 0f); + assert!(NaN.abs().is_NaN()); + } + + #[test] + fn test_abs_sub() { + assert_eq!((-1f).abs_sub(&1f), 0f); + assert_eq!(1f.abs_sub(&1f), 0f); + assert_eq!(1f.abs_sub(&0f), 1f); + assert_eq!(1f.abs_sub(&-1f), 2f); + assert_eq!(neg_infinity.abs_sub(&0f), 0f); + assert_eq!(infinity.abs_sub(&1f), infinity); + assert_eq!(0f.abs_sub(&neg_infinity), infinity); + assert_eq!(0f.abs_sub(&infinity), 0f); + assert!(NaN.abs_sub(&-1f).is_NaN()); + assert!(1f.abs_sub(&NaN).is_NaN()); + } + + #[test] + fn test_signum() { + assert_eq!(infinity.signum(), 1f); + assert_eq!(1f.signum(), 1f); + assert_eq!(0f.signum(), 1f); + assert_eq!((-0f).signum(), -1f); + assert_eq!((-1f).signum(), -1f); + assert_eq!(neg_infinity.signum(), -1f); + assert_eq!((1f/neg_infinity).signum(), -1f); + assert!(NaN.signum().is_NaN()); + } + + #[test] + fn test_is_positive() { + assert!(infinity.is_positive()); + assert!(1f.is_positive()); + assert!(0f.is_positive()); + assert!(!(-0f).is_positive()); + assert!(!(-1f).is_positive()); + assert!(!neg_infinity.is_positive()); + assert!(!(1f/neg_infinity).is_positive()); + assert!(!NaN.is_positive()); + } + + #[test] + fn test_is_negative() { + assert!(!infinity.is_negative()); + assert!(!1f.is_negative()); + assert!(!0f.is_negative()); + assert!((-0f).is_negative()); + assert!((-1f).is_negative()); + assert!(neg_infinity.is_negative()); + assert!((1f/neg_infinity).is_negative()); + assert!(!NaN.is_negative()); + } + + #[test] + fn test_approx_eq() { + assert!(1.0f.approx_eq(&1f)); + assert!(0.9999999f.approx_eq(&1f)); + assert!(1.000001f.approx_eq_eps(&1f, &1.0e-5)); + assert!(1.0000001f.approx_eq_eps(&1f, &1.0e-6)); + assert!(!1.0000001f.approx_eq_eps(&1f, &1.0e-7)); + } + + #[test] + fn test_primitive() { + assert_eq!(Primitive::bits::<float>(), sys::size_of::<float>() * 8); + assert_eq!(Primitive::bytes::<float>(), sys::size_of::<float>()); + } + + #[test] + fn test_is_normal() { + assert!(!Float::NaN::<float>().is_normal()); + assert!(!Float::infinity::<float>().is_normal()); + assert!(!Float::neg_infinity::<float>().is_normal()); + assert!(!Zero::zero::<float>().is_normal()); + assert!(!Float::neg_zero::<float>().is_normal()); + assert!(1f.is_normal()); + assert!(1e-307f.is_normal()); + assert!(!1e-308f.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::<float>().classify(), FPNaN); + assert_eq!(Float::infinity::<float>().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::<float>().classify(), FPInfinite); + assert_eq!(Zero::zero::<float>().classify(), FPZero); + assert_eq!(Float::neg_zero::<float>().classify(), FPZero); + assert_eq!(1f.classify(), FPNormal); + assert_eq!(1e-307f.classify(), FPNormal); + assert_eq!(1e-308f.classify(), FPSubnormal); + } + + #[test] + fn test_ldexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: float = from_str_hex("1p-123").unwrap(); + let f2: float = from_str_hex("1p-111").unwrap(); + assert_eq!(Float::ldexp(1f, -123), f1); + assert_eq!(Float::ldexp(1f, -111), f2); + + assert_eq!(Float::ldexp(0f, -123), 0f); + assert_eq!(Float::ldexp(-0f, -123), -0f); + assert_eq!(Float::ldexp(Float::infinity::<float>(), -123), + Float::infinity::<float>()); + assert_eq!(Float::ldexp(Float::neg_infinity::<float>(), -123), + Float::neg_infinity::<float>()); + assert!(Float::ldexp(Float::NaN::<float>(), -123).is_NaN()); + } + + #[test] + fn test_frexp() { + // We have to use from_str until base-2 exponents + // are supported in floating-point literals + let f1: float = from_str_hex("1p-123").unwrap(); + let f2: float = from_str_hex("1p-111").unwrap(); + let (x1, exp1) = f1.frexp(); + let (x2, exp2) = f2.frexp(); + assert_eq!((x1, exp1), (0.5f, -122)); + assert_eq!((x2, exp2), (0.5f, -110)); + assert_eq!(Float::ldexp(x1, exp1), f1); + assert_eq!(Float::ldexp(x2, exp2), f2); + + assert_eq!(0f.frexp(), (0f, 0)); + assert_eq!((-0f).frexp(), (-0f, 0)); + assert_eq!(match Float::infinity::<float>().frexp() { (x, _) => x }, + Float::infinity::<float>()) + assert_eq!(match Float::neg_infinity::<float>().frexp() { (x, _) => x }, + Float::neg_infinity::<float>()) + assert!(match Float::NaN::<float>().frexp() { (x, _) => x.is_NaN() }) + } + + #[test] + pub fn test_to_str_exact_do_decimal() { + let s = to_str_exact(5.0, 4u); + assert_eq!(s, ~"5.0000"); + } + + #[test] + pub fn test_from_str() { + assert_eq!(from_str(~"3"), Some(3.)); + assert_eq!(from_str(~"3.14"), Some(3.14)); + assert_eq!(from_str(~"+3.14"), Some(3.14)); + assert_eq!(from_str(~"-3.14"), Some(-3.14)); + assert_eq!(from_str(~"2.5E10"), Some(25000000000.)); + assert_eq!(from_str(~"2.5e10"), Some(25000000000.)); + assert_eq!(from_str(~"25000000000.E-10"), Some(2.5)); + assert_eq!(from_str(~"."), Some(0.)); + assert_eq!(from_str(~".e1"), Some(0.)); + assert_eq!(from_str(~".e-1"), Some(0.)); + assert_eq!(from_str(~"5."), Some(5.)); + assert_eq!(from_str(~".5"), Some(0.5)); + assert_eq!(from_str(~"0.5"), Some(0.5)); + assert_eq!(from_str(~"-.5"), Some(-0.5)); + assert_eq!(from_str(~"-5"), Some(-5.)); + assert_eq!(from_str(~"inf"), Some(infinity)); + assert_eq!(from_str(~"+inf"), Some(infinity)); + assert_eq!(from_str(~"-inf"), Some(neg_infinity)); + // note: NaN != NaN, hence this slightly complex test + match from_str(~"NaN") { + Some(f) => assert!(f.is_NaN()), + None => fail!() + } + // note: -0 == 0, hence these slightly more complex tests + match from_str(~"-0") { + Some(v) if v.is_zero() => assert!(v.is_negative()), + _ => fail!() + } + match from_str(~"0") { + Some(v) if v.is_zero() => assert!(v.is_positive()), + _ => fail!() + } + + assert!(from_str(~"").is_none()); + assert!(from_str(~"x").is_none()); + assert!(from_str(~" ").is_none()); + assert!(from_str(~" ").is_none()); + assert!(from_str(~"e").is_none()); + assert!(from_str(~"E").is_none()); + assert!(from_str(~"E1").is_none()); + assert!(from_str(~"1e1e1").is_none()); + assert!(from_str(~"1e1.1").is_none()); + assert!(from_str(~"1e1-1").is_none()); + } + + #[test] + pub fn test_from_str_hex() { + assert_eq!(from_str_hex(~"a4"), Some(164.)); + assert_eq!(from_str_hex(~"a4.fe"), Some(164.9921875)); + assert_eq!(from_str_hex(~"-a4.fe"), Some(-164.9921875)); + assert_eq!(from_str_hex(~"+a4.fe"), Some(164.9921875)); + assert_eq!(from_str_hex(~"ff0P4"), Some(0xff00 as float)); + assert_eq!(from_str_hex(~"ff0p4"), Some(0xff00 as float)); + assert_eq!(from_str_hex(~"ff0p-4"), Some(0xff as float)); + assert_eq!(from_str_hex(~"."), Some(0.)); + assert_eq!(from_str_hex(~".p1"), Some(0.)); + assert_eq!(from_str_hex(~".p-1"), Some(0.)); + assert_eq!(from_str_hex(~"f."), Some(15.)); + assert_eq!(from_str_hex(~".f"), Some(0.9375)); + assert_eq!(from_str_hex(~"0.f"), Some(0.9375)); + assert_eq!(from_str_hex(~"-.f"), Some(-0.9375)); + assert_eq!(from_str_hex(~"-f"), Some(-15.)); + assert_eq!(from_str_hex(~"inf"), Some(infinity)); + assert_eq!(from_str_hex(~"+inf"), Some(infinity)); + assert_eq!(from_str_hex(~"-inf"), Some(neg_infinity)); + // note: NaN != NaN, hence this slightly complex test + match from_str_hex(~"NaN") { + Some(f) => assert!(f.is_NaN()), + None => fail!() + } + // note: -0 == 0, hence these slightly more complex tests + match from_str_hex(~"-0") { + Some(v) if v.is_zero() => assert!(v.is_negative()), + _ => fail!() + } + match from_str_hex(~"0") { + Some(v) if v.is_zero() => assert!(v.is_positive()), + _ => fail!() + } + assert_eq!(from_str_hex(~"e"), Some(14.)); + assert_eq!(from_str_hex(~"E"), Some(14.)); + assert_eq!(from_str_hex(~"E1"), Some(225.)); + assert_eq!(from_str_hex(~"1e1e1"), Some(123361.)); + assert_eq!(from_str_hex(~"1e1.1"), Some(481.0625)); + + assert!(from_str_hex(~"").is_none()); + assert!(from_str_hex(~"x").is_none()); + assert!(from_str_hex(~" ").is_none()); + assert!(from_str_hex(~" ").is_none()); + assert!(from_str_hex(~"p").is_none()); + assert!(from_str_hex(~"P").is_none()); + assert!(from_str_hex(~"P1").is_none()); + assert!(from_str_hex(~"1p1p1").is_none()); + assert!(from_str_hex(~"1p1.1").is_none()); + assert!(from_str_hex(~"1p1-1").is_none()); + } + + #[test] + pub fn test_to_str_hex() { + assert_eq!(to_str_hex(164.), ~"a4"); + assert_eq!(to_str_hex(164.9921875), ~"a4.fe"); + assert_eq!(to_str_hex(-164.9921875), ~"-a4.fe"); + assert_eq!(to_str_hex(0xff00 as float), ~"ff00"); + assert_eq!(to_str_hex(-(0xff00 as float)), ~"-ff00"); + assert_eq!(to_str_hex(0.), ~"0"); + assert_eq!(to_str_hex(15.), ~"f"); + assert_eq!(to_str_hex(-15.), ~"-f"); + assert_eq!(to_str_hex(0.9375), ~"0.f"); + assert_eq!(to_str_hex(-0.9375), ~"-0.f"); + assert_eq!(to_str_hex(infinity), ~"inf"); + assert_eq!(to_str_hex(neg_infinity), ~"-inf"); + assert_eq!(to_str_hex(NaN), ~"NaN"); + assert_eq!(to_str_hex(0.), ~"0"); + assert_eq!(to_str_hex(-0.), ~"-0"); + } + + #[test] + pub fn test_to_str_radix() { + assert_eq!(to_str_radix(36., 36u), ~"10"); + assert_eq!(to_str_radix(8.125, 2u), ~"1000.001"); + } + + #[test] + pub fn test_from_str_radix() { + assert_eq!(from_str_radix(~"10", 36u), Some(36.)); + assert_eq!(from_str_radix(~"1000.001", 2u), Some(8.125)); + } + + #[test] + pub fn test_to_str_inf() { + assert_eq!(to_str_digits(infinity, 10u), ~"inf"); + assert_eq!(to_str_digits(-infinity, 10u), ~"-inf"); + } +} diff --git a/src/libstd/num/int-template.rs b/src/libstd/num/int-template.rs new file mode 100644 index 00000000000..3c31a7d5745 --- /dev/null +++ b/src/libstd/num/int-template.rs @@ -0,0 +1,929 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 T = self::inst::T; + +use num::{ToStrRadix, FromStrRadix}; +use num::{Zero, One, strconv}; +use prelude::*; + +pub use cmp::{min, max}; + +pub static bits : uint = inst::bits; +pub static bytes : uint = (inst::bits / 8); + +pub static min_value: T = (-1 as T) << (bits - 1); +pub static max_value: T = min_value - 1 as T; + +#[inline(always)] +pub fn add(x: T, y: T) -> T { x + y } +#[inline(always)] +pub fn sub(x: T, y: T) -> T { x - y } +#[inline(always)] +pub fn mul(x: T, y: T) -> T { x * y } +#[inline(always)] +pub fn div(x: T, y: T) -> T { x / y } + +/// +/// Returns the remainder of y / x. +/// +/// # Examples +/// ~~~ +/// assert!(int::rem(5 / 2) == 1); +/// ~~~ +/// +/// When faced with negative numbers, the result copies the sign of the +/// dividend. +/// +/// ~~~ +/// assert!(int::rem(2 / -3) == 2); +/// ~~~ +/// +/// ~~~ +/// assert!(int::rem(-2 / 3) == -2); +/// ~~~ +/// +/// +#[inline(always)] +pub fn rem(x: T, y: T) -> T { x % y } + +#[inline(always)] +pub fn lt(x: T, y: T) -> bool { x < y } +#[inline(always)] +pub fn le(x: T, y: T) -> bool { x <= y } +#[inline(always)] +pub fn eq(x: T, y: T) -> bool { x == y } +#[inline(always)] +pub fn ne(x: T, y: T) -> bool { x != y } +#[inline(always)] +pub fn ge(x: T, y: T) -> bool { x >= y } +#[inline(always)] +pub fn gt(x: T, y: T) -> bool { x > y } + +/// +/// Iterate over the range [`lo`..`hi`) +/// +/// # Arguments +/// +/// * `lo` - lower bound, inclusive +/// * `hi` - higher bound, exclusive +/// +/// # Examples +/// ~~~ +/// let mut sum = 0; +/// for int::range(1, 5) |i| { +/// sum += i; +/// } +/// assert!(sum == 10); +/// ~~~ +/// +#[inline(always)] +/// Iterate over the range [`start`,`start`+`step`..`stop`) +pub fn _range_step(start: T, stop: T, step: T, it: &fn(T) -> bool) -> bool { + let mut i = start; + if step == 0 { + fail!("range_step called with step == 0"); + } else if step > 0 { // ascending + while i < stop { + if !it(i) { return false; } + // avoiding overflow. break if i + step > max_value + if i > max_value - step { return true; } + i += step; + } + } else { // descending + while i > stop { + if !it(i) { return false; } + // avoiding underflow. break if i + step < min_value + if i < min_value - step { return true; } + i += step; + } + } + return true; +} + +pub fn range_step(start: T, stop: T, step: T, it: &fn(T) -> bool) -> bool { + _range_step(start, stop, step, it) +} + +#[inline(always)] +/// Iterate over the range [`lo`..`hi`) +pub fn range(lo: T, hi: T, it: &fn(T) -> bool) -> bool { + range_step(lo, hi, 1 as T, it) +} + +#[inline(always)] +/// Iterate over the range [`hi`..`lo`) +pub fn range_rev(hi: T, lo: T, it: &fn(T) -> bool) -> bool { + range_step(hi, lo, -1 as T, it) +} + +/// Computes the bitwise complement +#[inline(always)] +pub fn compl(i: T) -> T { + -1 as T ^ i +} + +/// Computes the absolute value +#[inline(always)] +pub fn abs(i: T) -> T { i.abs() } + +impl Num for T {} + +#[cfg(not(test))] +impl Ord for T { + #[inline(always)] + fn lt(&self, other: &T) -> bool { return (*self) < (*other); } + #[inline(always)] + fn le(&self, other: &T) -> bool { return (*self) <= (*other); } + #[inline(always)] + fn ge(&self, other: &T) -> bool { return (*self) >= (*other); } + #[inline(always)] + fn gt(&self, other: &T) -> bool { return (*self) > (*other); } +} + +#[cfg(not(test))] +impl Eq for T { + #[inline(always)] + fn eq(&self, other: &T) -> bool { return (*self) == (*other); } + #[inline(always)] + fn ne(&self, other: &T) -> bool { return (*self) != (*other); } +} + +impl Orderable for T { + #[inline(always)] + fn min(&self, other: &T) -> T { + if *self < *other { *self } else { *other } + } + + #[inline(always)] + fn max(&self, other: &T) -> T { + if *self > *other { *self } else { *other } + } + + /// Returns the number constrained within the range `mn <= self <= mx`. + #[inline(always)] + fn clamp(&self, mn: &T, mx: &T) -> T { + cond!( + (*self > *mx) { *mx } + (*self < *mn) { *mn } + _ { *self } + ) + } +} + +impl Zero for T { + #[inline(always)] + fn zero() -> T { 0 } + + #[inline(always)] + fn is_zero(&self) -> bool { *self == 0 } +} + +impl One for T { + #[inline(always)] + fn one() -> T { 1 } +} + +#[cfg(not(test))] +impl Add<T,T> for T { + #[inline(always)] + fn add(&self, other: &T) -> T { *self + *other } +} + +#[cfg(not(test))] +impl Sub<T,T> for T { + #[inline(always)] + fn sub(&self, other: &T) -> T { *self - *other } +} + +#[cfg(not(test))] +impl Mul<T,T> for T { + #[inline(always)] + fn mul(&self, other: &T) -> T { *self * *other } +} + +#[cfg(not(test))] +impl Div<T,T> for T { + /// + /// Integer division, truncated towards 0. As this behaviour reflects the underlying + /// machine implementation it is more efficient than `Integer::div_floor`. + /// + /// # Examples + /// + /// ~~~ + /// assert!( 8 / 3 == 2); + /// assert!( 8 / -3 == -2); + /// assert!(-8 / 3 == -2); + /// assert!(-8 / -3 == 2); + + /// assert!( 1 / 2 == 0); + /// assert!( 1 / -2 == 0); + /// assert!(-1 / 2 == 0); + /// assert!(-1 / -2 == 0); + /// ~~~ + /// + #[inline(always)] + fn div(&self, other: &T) -> T { *self / *other } +} + +#[cfg(not(test))] +impl Rem<T,T> for T { + /// + /// Returns the integer remainder after division, satisfying: + /// + /// ~~~ + /// assert!((n / d) * d + (n % d) == n) + /// ~~~ + /// + /// # Examples + /// + /// ~~~ + /// assert!( 8 % 3 == 2); + /// assert!( 8 % -3 == 2); + /// assert!(-8 % 3 == -2); + /// assert!(-8 % -3 == -2); + + /// assert!( 1 % 2 == 1); + /// assert!( 1 % -2 == 1); + /// assert!(-1 % 2 == -1); + /// assert!(-1 % -2 == -1); + /// ~~~ + /// + #[inline(always)] + fn rem(&self, other: &T) -> T { *self % *other } +} + +#[cfg(not(test))] +impl Neg<T> for T { + #[inline(always)] + fn neg(&self) -> T { -*self } +} + +impl Signed for T { + /// Computes the absolute value + #[inline(always)] + fn abs(&self) -> T { + if self.is_negative() { -*self } else { *self } + } + + /// + /// The positive difference of two numbers. Returns `0` if the number is less than or + /// equal to `other`, otherwise the difference between`self` and `other` is returned. + /// + #[inline(always)] + fn abs_sub(&self, other: &T) -> T { + if *self <= *other { 0 } else { *self - *other } + } + + /// + /// # Returns + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + #[inline(always)] + fn signum(&self) -> T { + match *self { + n if n > 0 => 1, + 0 => 0, + _ => -1, + } + } + + /// Returns true if the number is positive + #[inline(always)] + fn is_positive(&self) -> bool { *self > 0 } + + /// Returns true if the number is negative + #[inline(always)] + fn is_negative(&self) -> bool { *self < 0 } +} + +impl Integer for T { + /// + /// Floored integer division + /// + /// # Examples + /// + /// ~~~ + /// assert!(( 8).div_floor( 3) == 2); + /// assert!(( 8).div_floor(-3) == -3); + /// assert!((-8).div_floor( 3) == -3); + /// assert!((-8).div_floor(-3) == 2); + /// + /// assert!(( 1).div_floor( 2) == 0); + /// assert!(( 1).div_floor(-2) == -1); + /// assert!((-1).div_floor( 2) == -1); + /// assert!((-1).div_floor(-2) == 0); + /// ~~~ + /// + #[inline(always)] + fn div_floor(&self, other: &T) -> T { + // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, + // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + match self.div_rem(other) { + (d, r) if (r > 0 && *other < 0) + || (r < 0 && *other > 0) => d - 1, + (d, _) => d, + } + } + + /// + /// Integer modulo, satisfying: + /// + /// ~~~ + /// assert!(n.div_floor(d) * d + n.mod_floor(d) == n) + /// ~~~ + /// + /// # Examples + /// + /// ~~~ + /// assert!(( 8).mod_floor( 3) == 2); + /// assert!(( 8).mod_floor(-3) == -1); + /// assert!((-8).mod_floor( 3) == 1); + /// assert!((-8).mod_floor(-3) == -2); + /// + /// assert!(( 1).mod_floor( 2) == 1); + /// assert!(( 1).mod_floor(-2) == -1); + /// assert!((-1).mod_floor( 2) == 1); + /// assert!((-1).mod_floor(-2) == -1); + /// ~~~ + /// + #[inline(always)] + fn mod_floor(&self, other: &T) -> T { + // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, + // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + match *self % *other { + r if (r > 0 && *other < 0) + || (r < 0 && *other > 0) => r + *other, + r => r, + } + } + + /// Calculates `div_floor` and `mod_floor` simultaneously + #[inline(always)] + fn div_mod_floor(&self, other: &T) -> (T,T) { + // Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, + // December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) + match self.div_rem(other) { + (d, r) if (r > 0 && *other < 0) + || (r < 0 && *other > 0) => (d - 1, r + *other), + (d, r) => (d, r), + } + } + + /// Calculates `div` (`\`) and `rem` (`%`) simultaneously + #[inline(always)] + fn div_rem(&self, other: &T) -> (T,T) { + (*self / *other, *self % *other) + } + + /// + /// Calculates the Greatest Common Divisor (GCD) of the number and `other` + /// + /// The result is always positive + /// + #[inline(always)] + fn gcd(&self, other: &T) -> T { + // Use Euclid's algorithm + let mut m = *self, n = *other; + while m != 0 { + let temp = m; + m = n % temp; + n = temp; + } + n.abs() + } + + /// + /// Calculates the Lowest Common Multiple (LCM) of the number and `other` + /// + #[inline(always)] + fn lcm(&self, other: &T) -> T { + ((*self * *other) / self.gcd(other)).abs() // should not have to recaluculate abs + } + + /// Returns `true` if the number can be divided by `other` without leaving a remainder + #[inline(always)] + fn is_multiple_of(&self, other: &T) -> bool { *self % *other == 0 } + + /// Returns `true` if the number is divisible by `2` + #[inline(always)] + fn is_even(&self) -> bool { self.is_multiple_of(&2) } + + /// Returns `true` if the number is not divisible by `2` + #[inline(always)] + fn is_odd(&self) -> bool { !self.is_even() } +} + +impl Bitwise for T {} + +#[cfg(not(test))] +impl BitOr<T,T> for T { + #[inline(always)] + fn bitor(&self, other: &T) -> T { *self | *other } +} + +#[cfg(not(test))] +impl BitAnd<T,T> for T { + #[inline(always)] + fn bitand(&self, other: &T) -> T { *self & *other } +} + +#[cfg(not(test))] +impl BitXor<T,T> for T { + #[inline(always)] + fn bitxor(&self, other: &T) -> T { *self ^ *other } +} + +#[cfg(not(test))] +impl Shl<T,T> for T { + #[inline(always)] + fn shl(&self, other: &T) -> T { *self << *other } +} + +#[cfg(not(test))] +impl Shr<T,T> for T { + #[inline(always)] + fn shr(&self, other: &T) -> T { *self >> *other } +} + +#[cfg(not(test))] +impl Not<T> for T { + #[inline(always)] + fn not(&self) -> T { !*self } +} + +impl Bounded for T { + #[inline(always)] + fn min_value() -> T { min_value } + + #[inline(always)] + fn max_value() -> T { max_value } +} + +impl Int for T {} + +// String conversion functions and impl str -> num + +/// Parse a string as a number in base 10. +#[inline(always)] +pub fn from_str(s: &str) -> Option<T> { + strconv::from_str_common(s, 10u, true, false, false, + strconv::ExpNone, false, false) +} + +/// Parse a string as a number in the given base. +#[inline(always)] +pub fn from_str_radix(s: &str, radix: uint) -> Option<T> { + strconv::from_str_common(s, radix, true, false, false, + strconv::ExpNone, false, false) +} + +/// Parse a byte slice as a number in the given base. +#[inline(always)] +pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> { + strconv::from_str_bytes_common(buf, radix, true, false, false, + strconv::ExpNone, false, false) +} + +impl FromStr for T { + #[inline(always)] + fn from_str(s: &str) -> Option<T> { + from_str(s) + } +} + +impl FromStrRadix for T { + #[inline(always)] + fn from_str_radix(s: &str, radix: uint) -> Option<T> { + from_str_radix(s, radix) + } +} + +// String conversion functions and impl num -> str + +/// Convert to a string as a byte slice in a given base. +#[inline(always)] +pub fn to_str_bytes<U>(n: T, radix: uint, f: &fn(v: &[u8]) -> U) -> U { + let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, + strconv::SignNeg, strconv::DigAll); + f(buf) +} + +/// Convert to a string in base 10. +#[inline(always)] +pub fn to_str(num: T) -> ~str { + let (buf, _) = strconv::to_str_common(&num, 10u, false, + strconv::SignNeg, strconv::DigAll); + buf +} + +/// Convert to a string in a given base. +#[inline(always)] +pub fn to_str_radix(num: T, radix: uint) -> ~str { + let (buf, _) = strconv::to_str_common(&num, radix, false, + strconv::SignNeg, strconv::DigAll); + buf +} + +impl ToStr for T { + #[inline(always)] + fn to_str(&self) -> ~str { + to_str(*self) + } +} + +impl ToStrRadix for T { + #[inline(always)] + fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::inst::T; + use prelude::*; + + #[test] + fn test_num() { + num::test_num(10 as T, 2 as T); + } + + #[test] + fn test_orderable() { + assert_eq!((1 as T).min(&(2 as T)), 1 as T); + assert_eq!((2 as T).min(&(1 as T)), 1 as T); + assert_eq!((1 as T).max(&(2 as T)), 2 as T); + assert_eq!((2 as T).max(&(1 as T)), 2 as T); + assert_eq!((1 as T).clamp(&(2 as T), &(4 as T)), 2 as T); + assert_eq!((8 as T).clamp(&(2 as T), &(4 as T)), 4 as T); + assert_eq!((3 as T).clamp(&(2 as T), &(4 as T)), 3 as T); + } + + #[test] + pub fn test_abs() { + assert_eq!((1 as T).abs(), 1 as T); + assert_eq!((0 as T).abs(), 0 as T); + assert_eq!((-1 as T).abs(), 1 as T); + } + + #[test] + fn test_abs_sub() { + assert_eq!((-1 as T).abs_sub(&(1 as T)), 0 as T); + assert_eq!((1 as T).abs_sub(&(1 as T)), 0 as T); + assert_eq!((1 as T).abs_sub(&(0 as T)), 1 as T); + assert_eq!((1 as T).abs_sub(&(-1 as T)), 2 as T); + } + + #[test] + fn test_signum() { + assert_eq!((1 as T).signum(), 1 as T); + assert_eq!((0 as T).signum(), 0 as T); + assert_eq!((-0 as T).signum(), 0 as T); + assert_eq!((-1 as T).signum(), -1 as T); + } + + #[test] + fn test_is_positive() { + assert!((1 as T).is_positive()); + assert!(!(0 as T).is_positive()); + assert!(!(-0 as T).is_positive()); + assert!(!(-1 as T).is_positive()); + } + + #[test] + fn test_is_negative() { + assert!(!(1 as T).is_negative()); + assert!(!(0 as T).is_negative()); + assert!(!(-0 as T).is_negative()); + assert!((-1 as T).is_negative()); + } + + /// + /// Checks that the division rule holds for: + /// + /// - `n`: numerator (dividend) + /// - `d`: denominator (divisor) + /// - `qr`: quotient and remainder + /// + #[cfg(test)] + fn test_division_rule((n,d): (T,T), (q,r): (T,T)) { + assert_eq!(d * q + r, n); + } + + #[test] + fn test_div_rem() { + fn test_nd_dr(nd: (T,T), qr: (T,T)) { + let (n,d) = nd; + let separate_div_rem = (n / d, n % d); + let combined_div_rem = n.div_rem(&d); + + assert_eq!(separate_div_rem, qr); + assert_eq!(combined_div_rem, qr); + + test_division_rule(nd, separate_div_rem); + test_division_rule(nd, combined_div_rem); + } + + test_nd_dr(( 8, 3), ( 2, 2)); + test_nd_dr(( 8, -3), (-2, 2)); + test_nd_dr((-8, 3), (-2, -2)); + test_nd_dr((-8, -3), ( 2, -2)); + + test_nd_dr(( 1, 2), ( 0, 1)); + test_nd_dr(( 1, -2), ( 0, 1)); + test_nd_dr((-1, 2), ( 0, -1)); + test_nd_dr((-1, -2), ( 0, -1)); + } + + #[test] + fn test_div_mod_floor() { + fn test_nd_dm(nd: (T,T), dm: (T,T)) { + let (n,d) = nd; + let separate_div_mod_floor = (n.div_floor(&d), n.mod_floor(&d)); + let combined_div_mod_floor = n.div_mod_floor(&d); + + assert_eq!(separate_div_mod_floor, dm); + assert_eq!(combined_div_mod_floor, dm); + + test_division_rule(nd, separate_div_mod_floor); + test_division_rule(nd, combined_div_mod_floor); + } + + test_nd_dm(( 8, 3), ( 2, 2)); + test_nd_dm(( 8, -3), (-3, -1)); + test_nd_dm((-8, 3), (-3, 1)); + test_nd_dm((-8, -3), ( 2, -2)); + + test_nd_dm(( 1, 2), ( 0, 1)); + test_nd_dm(( 1, -2), (-1, -1)); + test_nd_dm((-1, 2), (-1, 1)); + test_nd_dm((-1, -2), ( 0, -1)); + } + + #[test] + fn test_gcd() { + assert_eq!((10 as T).gcd(&2), 2 as T); + assert_eq!((10 as T).gcd(&3), 1 as T); + assert_eq!((0 as T).gcd(&3), 3 as T); + assert_eq!((3 as T).gcd(&3), 3 as T); + assert_eq!((56 as T).gcd(&42), 14 as T); + assert_eq!((3 as T).gcd(&-3), 3 as T); + assert_eq!((-6 as T).gcd(&3), 3 as T); + assert_eq!((-4 as T).gcd(&-2), 2 as T); + } + + #[test] + fn test_lcm() { + assert_eq!((1 as T).lcm(&0), 0 as T); + assert_eq!((0 as T).lcm(&1), 0 as T); + assert_eq!((1 as T).lcm(&1), 1 as T); + assert_eq!((-1 as T).lcm(&1), 1 as T); + assert_eq!((1 as T).lcm(&-1), 1 as T); + assert_eq!((-1 as T).lcm(&-1), 1 as T); + assert_eq!((8 as T).lcm(&9), 72 as T); + assert_eq!((11 as T).lcm(&5), 55 as T); + } + + #[test] + fn test_bitwise() { + assert_eq!(0b1110 as T, (0b1100 as T).bitor(&(0b1010 as T))); + assert_eq!(0b1000 as T, (0b1100 as T).bitand(&(0b1010 as T))); + assert_eq!(0b0110 as T, (0b1100 as T).bitxor(&(0b1010 as T))); + assert_eq!(0b1110 as T, (0b0111 as T).shl(&(1 as T))); + assert_eq!(0b0111 as T, (0b1110 as T).shr(&(1 as T))); + assert_eq!(-(0b11 as T) - (1 as T), (0b11 as T).not()); + } + + #[test] + fn test_multiple_of() { + assert!((6 as T).is_multiple_of(&(6 as T))); + assert!((6 as T).is_multiple_of(&(3 as T))); + assert!((6 as T).is_multiple_of(&(1 as T))); + assert!((-8 as T).is_multiple_of(&(4 as T))); + assert!((8 as T).is_multiple_of(&(-1 as T))); + assert!((-8 as T).is_multiple_of(&(-2 as T))); + } + + #[test] + fn test_even() { + assert_eq!((-4 as T).is_even(), true); + assert_eq!((-3 as T).is_even(), false); + assert_eq!((-2 as T).is_even(), true); + assert_eq!((-1 as T).is_even(), false); + assert_eq!((0 as T).is_even(), true); + assert_eq!((1 as T).is_even(), false); + assert_eq!((2 as T).is_even(), true); + assert_eq!((3 as T).is_even(), false); + assert_eq!((4 as T).is_even(), true); + } + + #[test] + fn test_odd() { + assert_eq!((-4 as T).is_odd(), false); + assert_eq!((-3 as T).is_odd(), true); + assert_eq!((-2 as T).is_odd(), false); + assert_eq!((-1 as T).is_odd(), true); + assert_eq!((0 as T).is_odd(), false); + assert_eq!((1 as T).is_odd(), true); + assert_eq!((2 as T).is_odd(), false); + assert_eq!((3 as T).is_odd(), true); + assert_eq!((4 as T).is_odd(), false); + } + + #[test] + fn test_bitcount() { + assert_eq!((0b010101 as T).population_count(), 3); + } + + #[test] + fn test_primitive() { + assert_eq!(Primitive::bits::<T>(), sys::size_of::<T>() * 8); + assert_eq!(Primitive::bytes::<T>(), sys::size_of::<T>()); + } + + #[test] + fn test_from_str() { + assert_eq!(from_str(~"0"), Some(0 as T)); + assert_eq!(from_str(~"3"), Some(3 as T)); + assert_eq!(from_str(~"10"), Some(10 as T)); + assert_eq!(i32::from_str(~"123456789"), Some(123456789 as i32)); + assert_eq!(from_str(~"00100"), Some(100 as T)); + + assert_eq!(from_str(~"-1"), Some(-1 as T)); + assert_eq!(from_str(~"-3"), Some(-3 as T)); + assert_eq!(from_str(~"-10"), Some(-10 as T)); + assert_eq!(i32::from_str(~"-123456789"), Some(-123456789 as i32)); + assert_eq!(from_str(~"-00100"), Some(-100 as T)); + + assert!(from_str(~" ").is_none()); + assert!(from_str(~"x").is_none()); + } + + #[test] + fn test_parse_bytes() { + use str::to_bytes; + assert_eq!(parse_bytes(to_bytes(~"123"), 10u), Some(123 as T)); + assert_eq!(parse_bytes(to_bytes(~"1001"), 2u), Some(9 as T)); + assert_eq!(parse_bytes(to_bytes(~"123"), 8u), Some(83 as T)); + assert_eq!(i32::parse_bytes(to_bytes(~"123"), 16u), Some(291 as i32)); + assert_eq!(i32::parse_bytes(to_bytes(~"ffff"), 16u), Some(65535 as i32)); + assert_eq!(i32::parse_bytes(to_bytes(~"FFFF"), 16u), Some(65535 as i32)); + assert_eq!(parse_bytes(to_bytes(~"z"), 36u), Some(35 as T)); + assert_eq!(parse_bytes(to_bytes(~"Z"), 36u), Some(35 as T)); + + assert_eq!(parse_bytes(to_bytes(~"-123"), 10u), Some(-123 as T)); + assert_eq!(parse_bytes(to_bytes(~"-1001"), 2u), Some(-9 as T)); + assert_eq!(parse_bytes(to_bytes(~"-123"), 8u), Some(-83 as T)); + assert_eq!(i32::parse_bytes(to_bytes(~"-123"), 16u), Some(-291 as i32)); + assert_eq!(i32::parse_bytes(to_bytes(~"-ffff"), 16u), Some(-65535 as i32)); + assert_eq!(i32::parse_bytes(to_bytes(~"-FFFF"), 16u), Some(-65535 as i32)); + assert_eq!(parse_bytes(to_bytes(~"-z"), 36u), Some(-35 as T)); + assert_eq!(parse_bytes(to_bytes(~"-Z"), 36u), Some(-35 as T)); + + assert!(parse_bytes(to_bytes(~"Z"), 35u).is_none()); + assert!(parse_bytes(to_bytes(~"-9"), 2u).is_none()); + } + + #[test] + fn test_to_str() { + assert_eq!(to_str_radix(0 as T, 10u), ~"0"); + assert_eq!(to_str_radix(1 as T, 10u), ~"1"); + assert_eq!(to_str_radix(-1 as T, 10u), ~"-1"); + assert_eq!(to_str_radix(127 as T, 16u), ~"7f"); + assert_eq!(to_str_radix(100 as T, 10u), ~"100"); + + } + + #[test] + fn test_int_to_str_overflow() { + let mut i8_val: i8 = 127_i8; + assert_eq!(i8::to_str(i8_val), ~"127"); + + i8_val += 1 as i8; + assert_eq!(i8::to_str(i8_val), ~"-128"); + + let mut i16_val: i16 = 32_767_i16; + assert_eq!(i16::to_str(i16_val), ~"32767"); + + i16_val += 1 as i16; + assert_eq!(i16::to_str(i16_val), ~"-32768"); + + let mut i32_val: i32 = 2_147_483_647_i32; + assert_eq!(i32::to_str(i32_val), ~"2147483647"); + + i32_val += 1 as i32; + assert_eq!(i32::to_str(i32_val), ~"-2147483648"); + + let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; + assert_eq!(i64::to_str(i64_val), ~"9223372036854775807"); + + i64_val += 1 as i64; + assert_eq!(i64::to_str(i64_val), ~"-9223372036854775808"); + } + + #[test] + fn test_int_from_str_overflow() { + let mut i8_val: i8 = 127_i8; + assert_eq!(i8::from_str(~"127"), Some(i8_val)); + assert!(i8::from_str(~"128").is_none()); + + i8_val += 1 as i8; + assert_eq!(i8::from_str(~"-128"), Some(i8_val)); + assert!(i8::from_str(~"-129").is_none()); + + let mut i16_val: i16 = 32_767_i16; + assert_eq!(i16::from_str(~"32767"), Some(i16_val)); + assert!(i16::from_str(~"32768").is_none()); + + i16_val += 1 as i16; + assert_eq!(i16::from_str(~"-32768"), Some(i16_val)); + assert!(i16::from_str(~"-32769").is_none()); + + let mut i32_val: i32 = 2_147_483_647_i32; + assert_eq!(i32::from_str(~"2147483647"), Some(i32_val)); + assert!(i32::from_str(~"2147483648").is_none()); + + i32_val += 1 as i32; + assert_eq!(i32::from_str(~"-2147483648"), Some(i32_val)); + assert!(i32::from_str(~"-2147483649").is_none()); + + let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; + assert_eq!(i64::from_str(~"9223372036854775807"), Some(i64_val)); + assert!(i64::from_str(~"9223372036854775808").is_none()); + + i64_val += 1 as i64; + assert_eq!(i64::from_str(~"-9223372036854775808"), Some(i64_val)); + assert!(i64::from_str(~"-9223372036854775809").is_none()); + } + + #[test] + fn test_ranges() { + let mut l = ~[]; + + for range(0,3) |i| { + l.push(i); + } + for range_rev(13,10) |i| { + l.push(i); + } + for range_step(20,26,2) |i| { + l.push(i); + } + for range_step(36,30,-2) |i| { + l.push(i); + } + for range_step(max_value - 2, max_value, 2) |i| { + l.push(i); + } + for range_step(max_value - 3, max_value, 2) |i| { + l.push(i); + } + for range_step(min_value + 2, min_value, -2) |i| { + l.push(i); + } + for range_step(min_value + 3, min_value, -2) |i| { + l.push(i); + } + assert_eq!(l, ~[0,1,2, + 13,12,11, + 20,22,24, + 36,34,32, + max_value-2, + max_value-3,max_value-1, + min_value+2, + min_value+3,min_value+1]); + + // None of the `fail`s should execute. + for range(10,0) |_i| { + fail!("unreachable"); + } + for range_rev(0,10) |_i| { + fail!("unreachable"); + } + for range_step(10,0,1) |_i| { + fail!("unreachable"); + } + for range_step(0,10,-1) |_i| { + fail!("unreachable"); + } + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_range_step_zero_step() { + for range_step(0,10,0) |_i| {} + } +} diff --git a/src/libstd/num/int-template/i16.rs b/src/libstd/num/int-template/i16.rs new file mode 100644 index 00000000000..28263378555 --- /dev/null +++ b/src/libstd/num/int-template/i16.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `i16` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = i16; + pub static bits: uint = ::u16::bits; + + impl Primitive for i16 { + #[inline(always)] + fn bits() -> uint { 16 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<i16>() / 8 } + } + + impl BitCount for i16 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> i16 { unsafe { intrinsics::ctpop16(*self) } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> i16 { unsafe { intrinsics::ctlz16(*self) } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> i16 { unsafe { intrinsics::cttz16(*self) } } + } +} diff --git a/src/libstd/num/int-template/i32.rs b/src/libstd/num/int-template/i32.rs new file mode 100644 index 00000000000..959cf8f7d77 --- /dev/null +++ b/src/libstd/num/int-template/i32.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `i32` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = i32; + pub static bits: uint = ::u32::bits; + + impl Primitive for i32 { + #[inline(always)] + fn bits() -> uint { 32 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<i32>() / 8 } + } + + impl BitCount for i32 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> i32 { unsafe { intrinsics::ctpop32(*self) } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> i32 { unsafe { intrinsics::ctlz32(*self) } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> i32 { unsafe { intrinsics::cttz32(*self) } } + } +} diff --git a/src/libstd/num/int-template/i64.rs b/src/libstd/num/int-template/i64.rs new file mode 100644 index 00000000000..3b51c70be12 --- /dev/null +++ b/src/libstd/num/int-template/i64.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `i64` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = i64; + pub static bits: uint = ::u64::bits; + + impl Primitive for i64 { + #[inline(always)] + fn bits() -> uint { 64 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<i64>() / 8 } + } + + impl BitCount for i64 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> i64 { unsafe { intrinsics::ctpop64(*self) } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> i64 { unsafe { intrinsics::ctlz64(*self) } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> i64 { unsafe { intrinsics::cttz64(*self) } } + } +} diff --git a/src/libstd/num/int-template/i8.rs b/src/libstd/num/int-template/i8.rs new file mode 100644 index 00000000000..896fb4dbf50 --- /dev/null +++ b/src/libstd/num/int-template/i8.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `i8` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = i8; + pub static bits: uint = ::u8::bits; + + impl Primitive for i8 { + #[inline(always)] + fn bits() -> uint { 8 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<i8>() / 8 } + } + + impl BitCount for i8 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> i8 { unsafe { intrinsics::ctpop8(*self) } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> i8 { unsafe { intrinsics::ctlz8(*self) } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> i8 { unsafe { intrinsics::cttz8(*self) } } + } +} diff --git a/src/libstd/num/int-template/int.rs b/src/libstd/num/int-template/int.rs new file mode 100644 index 00000000000..64f6f2d2a9a --- /dev/null +++ b/src/libstd/num/int-template/int.rs @@ -0,0 +1,104 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `int` + +pub use self::inst::pow; + +mod inst { + use num::{Primitive, BitCount}; + + pub type T = int; + pub static bits: uint = ::uint::bits; + + impl Primitive for int { + #[cfg(target_word_size = "32")] + #[inline(always)] + fn bits() -> uint { 32 } + + #[cfg(target_word_size = "64")] + #[inline(always)] + fn bits() -> uint { 64 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<int>() / 8 } + } + + #[cfg(target_word_size = "32")] + #[inline(always)] + impl BitCount for int { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> int { (*self as i32).population_count() as int } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> int { (*self as i32).leading_zeros() as int } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> int { (*self as i32).trailing_zeros() as int } + } + + #[cfg(target_word_size = "64")] + #[inline(always)] + impl BitCount for int { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> int { (*self as i64).population_count() as int } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> int { (*self as i64).leading_zeros() as int } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> int { (*self as i64).trailing_zeros() as int } + } + + /// Returns `base` raised to the power of `exponent` + pub fn pow(base: int, exponent: uint) -> int { + if exponent == 0u { + //Not mathemtically true if ~[base == 0] + return 1; + } + if base == 0 { return 0; } + let mut my_pow = exponent; + let mut acc = 1; + let mut multiplier = base; + while(my_pow > 0u) { + if my_pow % 2u == 1u { + acc *= multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + return acc; + } + + #[test] + fn test_pow() { + assert_eq!(pow(0, 0u), 1); + assert_eq!(pow(0, 1u), 0); + assert_eq!(pow(0, 2u), 0); + assert_eq!(pow(-1, 0u), 1); + assert_eq!(pow(1, 0u), 1); + assert_eq!(pow(-3, 2u), 9); + assert_eq!(pow(-3, 3u), -27); + assert_eq!(pow(4, 9u), 262144); + } + + #[test] + fn test_overflows() { + assert!((::int::max_value > 0)); + assert!((::int::min_value <= 0)); + assert_eq!(::int::min_value + ::int::max_value + 1, 0); + } +} diff --git a/src/libstd/num/num.rs b/src/libstd/num/num.rs new file mode 100644 index 00000000000..96b302d3174 --- /dev/null +++ b/src/libstd/num/num.rs @@ -0,0 +1,494 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! An interface for numeric types +use cmp::{Eq, ApproxEq, Ord}; +use ops::{Add, Sub, Mul, Div, Rem, Neg}; +use ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr}; +use option::Option; +use kinds::Copy; + +pub mod strconv; + +/// +/// The base trait for numeric types +/// +pub trait Num: Eq + Zero + One + + Neg<Self> + + Add<Self,Self> + + Sub<Self,Self> + + Mul<Self,Self> + + Div<Self,Self> + + Rem<Self,Self> {} + +pub trait IntConvertible { + fn to_int(&self) -> int; + fn from_int(n: int) -> Self; +} + +pub trait Orderable: Ord { + // These should be methods on `Ord`, with overridable default implementations. We don't want + // to encumber all implementors of Ord by requiring them to implement these functions, but at + // the same time we want to be able to take advantage of the speed of the specific numeric + // functions (like the `fmin` and `fmax` intrinsics). + fn min(&self, other: &Self) -> Self; + fn max(&self, other: &Self) -> Self; + fn clamp(&self, mn: &Self, mx: &Self) -> Self; +} + +pub trait Zero { + fn zero() -> Self; // FIXME (#5527): This should be an associated constant + fn is_zero(&self) -> bool; +} + +pub trait One { + fn one() -> Self; // FIXME (#5527): This should be an associated constant +} + +pub trait Signed: Num + + Neg<Self> { + fn abs(&self) -> Self; + fn abs_sub(&self, other: &Self) -> Self; + fn signum(&self) -> Self; + + fn is_positive(&self) -> bool; + fn is_negative(&self) -> bool; +} + +pub trait Unsigned: Num {} + +// This should be moved into the default implementation for Signed::abs +pub fn abs<T:Ord + Zero + Neg<T>>(v: T) -> T { + if v < Zero::zero() { v.neg() } else { v } +} + +pub trait Integer: Num + + Orderable + + Div<Self,Self> + + Rem<Self,Self> { + fn div_rem(&self, other: &Self) -> (Self,Self); + + fn div_floor(&self, other: &Self) -> Self; + fn mod_floor(&self, other: &Self) -> Self; + fn div_mod_floor(&self, other: &Self) -> (Self,Self); + + fn gcd(&self, other: &Self) -> Self; + fn lcm(&self, other: &Self) -> Self; + + fn is_multiple_of(&self, other: &Self) -> bool; + fn is_even(&self) -> bool; + fn is_odd(&self) -> bool; +} + +pub trait Round { + fn floor(&self) -> Self; + fn ceil(&self) -> Self; + fn round(&self) -> Self; + fn trunc(&self) -> Self; + fn fract(&self) -> Self; +} + +pub trait Fractional: Num + + Orderable + + Round + + Div<Self,Self> { + fn recip(&self) -> Self; +} + +pub trait Algebraic { + fn pow(&self, n: Self) -> Self; + fn sqrt(&self) -> Self; + fn rsqrt(&self) -> Self; + fn cbrt(&self) -> Self; + fn hypot(&self, other: Self) -> Self; +} + +pub trait Trigonometric { + fn sin(&self) -> Self; + fn cos(&self) -> Self; + fn tan(&self) -> Self; + fn asin(&self) -> Self; + fn acos(&self) -> Self; + fn atan(&self) -> Self; + fn atan2(&self, other: Self) -> Self; + fn sin_cos(&self) -> (Self, Self); +} + +pub trait Exponential { + fn exp(&self) -> Self; + fn exp2(&self) -> Self; + fn ln(&self) -> Self; + fn log(&self, base: Self) -> Self; + fn log2(&self) -> Self; + fn log10(&self) -> Self; +} + +pub trait Hyperbolic: Exponential { + fn sinh(&self) -> Self; + fn cosh(&self) -> Self; + fn tanh(&self) -> Self; + fn asinh(&self) -> Self; + fn acosh(&self) -> Self; + fn atanh(&self) -> Self; +} + +/// +/// Defines constants and methods common to real numbers +/// +pub trait Real: Signed + + Fractional + + Algebraic + + Trigonometric + + Hyperbolic { + // Common Constants + // FIXME (#5527): These should be associated constants + fn pi() -> Self; + fn two_pi() -> Self; + fn frac_pi_2() -> Self; + fn frac_pi_3() -> Self; + fn frac_pi_4() -> Self; + fn frac_pi_6() -> Self; + fn frac_pi_8() -> Self; + fn frac_1_pi() -> Self; + fn frac_2_pi() -> Self; + fn frac_2_sqrtpi() -> Self; + fn sqrt2() -> Self; + fn frac_1_sqrt2() -> Self; + fn e() -> Self; + fn log2_e() -> Self; + fn log10_e() -> Self; + fn ln_2() -> Self; + fn ln_10() -> Self; + + // Angular conversions + fn to_degrees(&self) -> Self; + fn to_radians(&self) -> Self; +} + +/// +/// Methods that are harder to implement and not commonly used. +/// +pub trait RealExt: Real { + // FIXME (#5527): usages of `int` should be replaced with an associated + // integer type once these are implemented + + // Gamma functions + fn lgamma(&self) -> (int, Self); + fn tgamma(&self) -> Self; + + // Bessel functions + fn j0(&self) -> Self; + fn j1(&self) -> Self; + fn jn(&self, n: int) -> Self; + fn y0(&self) -> Self; + fn y1(&self) -> Self; + fn yn(&self, n: int) -> Self; +} + +/// +/// Collects the bitwise operators under one trait. +/// +pub trait Bitwise: Not<Self> + + BitAnd<Self,Self> + + BitOr<Self,Self> + + BitXor<Self,Self> + + Shl<Self,Self> + + Shr<Self,Self> {} + +pub trait BitCount { + fn population_count(&self) -> Self; + fn leading_zeros(&self) -> Self; + fn trailing_zeros(&self) -> Self; +} + +pub trait Bounded { + // FIXME (#5527): These should be associated constants + fn min_value() -> Self; + fn max_value() -> Self; +} + +/// +/// Specifies the available operations common to all of Rust's core numeric primitives. +/// These may not always make sense from a purely mathematical point of view, but +/// may be useful for systems programming. +/// +pub trait Primitive: Num + + NumCast + + Bounded + + Neg<Self> + + Add<Self,Self> + + Sub<Self,Self> + + Mul<Self,Self> + + Div<Self,Self> + + Rem<Self,Self> { + // FIXME (#5527): These should be associated constants + fn bits() -> uint; + fn bytes() -> uint; +} + +/// +/// A collection of traits relevant to primitive signed and unsigned integers +/// +pub trait Int: Integer + + Primitive + + Bitwise + + BitCount {} + +/// +/// Used for representing the classification of floating point numbers +/// +#[deriving(Eq)] +pub enum FPCategory { + /// "Not a Number", often obtained by dividing by zero + FPNaN, + /// Positive or negative infinity + FPInfinite , + /// Positive or negative zero + FPZero, + /// De-normalized floating point representation (less precise than `FPNormal`) + FPSubnormal, + /// A regular floating point number + FPNormal, +} + +/// +/// Primitive floating point numbers +/// +pub trait Float: Real + + Signed + + Primitive + + ApproxEq<Self> { + // FIXME (#5527): These should be associated constants + fn NaN() -> Self; + fn infinity() -> Self; + fn neg_infinity() -> Self; + fn neg_zero() -> Self; + + fn is_NaN(&self) -> bool; + fn is_infinite(&self) -> bool; + fn is_finite(&self) -> bool; + fn is_normal(&self) -> bool; + fn classify(&self) -> FPCategory; + + fn mantissa_digits() -> uint; + fn digits() -> uint; + fn epsilon() -> Self; + fn min_exp() -> int; + fn max_exp() -> int; + fn min_10_exp() -> int; + fn max_10_exp() -> int; + + fn ldexp(x: Self, exp: int) -> Self; + fn frexp(&self) -> (Self, int); + + fn exp_m1(&self) -> Self; + fn ln_1p(&self) -> Self; + fn mul_add(&self, a: Self, b: Self) -> Self; + fn next_after(&self, other: Self) -> Self; +} + +/// +/// Cast from one machine scalar to another +/// +/// # Example +/// +/// ~~~ +/// let twenty: f32 = num::cast(0x14); +/// assert_eq!(twenty, 20f32); +/// ~~~ +/// +#[inline(always)] +pub fn cast<T:NumCast,U:NumCast>(n: T) -> U { + NumCast::from(n) +} + +/// +/// An interface for casting between machine scalars +/// +pub trait NumCast { + fn from<T:NumCast>(n: T) -> Self; + + fn to_u8(&self) -> u8; + fn to_u16(&self) -> u16; + fn to_u32(&self) -> u32; + fn to_u64(&self) -> u64; + fn to_uint(&self) -> uint; + + fn to_i8(&self) -> i8; + fn to_i16(&self) -> i16; + fn to_i32(&self) -> i32; + fn to_i64(&self) -> i64; + fn to_int(&self) -> int; + + fn to_f32(&self) -> f32; + fn to_f64(&self) -> f64; + fn to_float(&self) -> float; +} + +macro_rules! impl_num_cast( + ($T:ty, $conv:ident) => ( + impl NumCast for $T { + #[inline(always)] + fn from<N:NumCast>(n: N) -> $T { + // `$conv` could be generated using `concat_idents!`, but that + // macro seems to be broken at the moment + n.$conv() + } + + #[inline(always)] fn to_u8(&self) -> u8 { *self as u8 } + #[inline(always)] fn to_u16(&self) -> u16 { *self as u16 } + #[inline(always)] fn to_u32(&self) -> u32 { *self as u32 } + #[inline(always)] fn to_u64(&self) -> u64 { *self as u64 } + #[inline(always)] fn to_uint(&self) -> uint { *self as uint } + + #[inline(always)] fn to_i8(&self) -> i8 { *self as i8 } + #[inline(always)] fn to_i16(&self) -> i16 { *self as i16 } + #[inline(always)] fn to_i32(&self) -> i32 { *self as i32 } + #[inline(always)] fn to_i64(&self) -> i64 { *self as i64 } + #[inline(always)] fn to_int(&self) -> int { *self as int } + + #[inline(always)] fn to_f32(&self) -> f32 { *self as f32 } + #[inline(always)] fn to_f64(&self) -> f64 { *self as f64 } + #[inline(always)] fn to_float(&self) -> float { *self as float } + } + ) +) + +impl_num_cast!(u8, to_u8) +impl_num_cast!(u16, to_u16) +impl_num_cast!(u32, to_u32) +impl_num_cast!(u64, to_u64) +impl_num_cast!(uint, to_uint) +impl_num_cast!(i8, to_i8) +impl_num_cast!(i16, to_i16) +impl_num_cast!(i32, to_i32) +impl_num_cast!(i64, to_i64) +impl_num_cast!(int, to_int) +impl_num_cast!(f32, to_f32) +impl_num_cast!(f64, to_f64) +impl_num_cast!(float, to_float) + +pub trait ToStrRadix { + pub fn to_str_radix(&self, radix: uint) -> ~str; +} + +pub trait FromStrRadix { + pub fn from_str_radix(str: &str, radix: uint) -> Option<Self>; +} + +/// +/// Calculates a power to a given radix, optimized for uint `pow` and `radix`. +/// +/// Returns `radix^pow` as `T`. +/// +/// Note: +/// Also returns `1` for `0^0`, despite that technically being an +/// undefined number. The reason for this is twofold: +/// - If code written to use this function cares about that special case, it's +/// probably going to catch it before making the call. +/// - If code written to use this function doesn't care about it, it's +/// probably assuming that `x^0` always equals `1`. +/// +pub fn pow_with_uint<T:NumCast+One+Zero+Copy+Div<T,T>+Mul<T,T>>(radix: uint, pow: uint) -> T { + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if pow == 0u { return _1; } + if radix == 0u { return _0; } + let mut my_pow = pow; + let mut total = _1; + let mut multiplier = cast(radix); + while (my_pow > 0u) { + if my_pow % 2u == 1u { + total *= multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + total +} + +/// Helper function for testing numeric operations +#[cfg(test)] +pub fn test_num<T:Num + NumCast>(ten: T, two: T) { + assert_eq!(ten.add(&two), cast(12)); + assert_eq!(ten.sub(&two), cast(8)); + assert_eq!(ten.mul(&two), cast(20)); + assert_eq!(ten.div(&two), cast(5)); + assert_eq!(ten.rem(&two), cast(0)); + + assert_eq!(ten.add(&two), ten + two); + assert_eq!(ten.sub(&two), ten - two); + assert_eq!(ten.mul(&two), ten * two); + assert_eq!(ten.div(&two), ten / two); + assert_eq!(ten.rem(&two), ten % two); +} + +macro_rules! test_cast_20( + ($_20:expr) => ({ + let _20 = $_20; + + assert_eq!(20u, _20.to_uint()); + assert_eq!(20u8, _20.to_u8()); + assert_eq!(20u16, _20.to_u16()); + assert_eq!(20u32, _20.to_u32()); + assert_eq!(20u64, _20.to_u64()); + assert_eq!(20i, _20.to_int()); + assert_eq!(20i8, _20.to_i8()); + assert_eq!(20i16, _20.to_i16()); + assert_eq!(20i32, _20.to_i32()); + assert_eq!(20i64, _20.to_i64()); + assert_eq!(20f, _20.to_float()); + assert_eq!(20f32, _20.to_f32()); + assert_eq!(20f64, _20.to_f64()); + + assert_eq!(_20, NumCast::from(20u)); + assert_eq!(_20, NumCast::from(20u8)); + assert_eq!(_20, NumCast::from(20u16)); + assert_eq!(_20, NumCast::from(20u32)); + assert_eq!(_20, NumCast::from(20u64)); + assert_eq!(_20, NumCast::from(20i)); + assert_eq!(_20, NumCast::from(20i8)); + assert_eq!(_20, NumCast::from(20i16)); + assert_eq!(_20, NumCast::from(20i32)); + assert_eq!(_20, NumCast::from(20i64)); + assert_eq!(_20, NumCast::from(20f)); + assert_eq!(_20, NumCast::from(20f32)); + assert_eq!(_20, NumCast::from(20f64)); + + assert_eq!(_20, cast(20u)); + assert_eq!(_20, cast(20u8)); + assert_eq!(_20, cast(20u16)); + assert_eq!(_20, cast(20u32)); + assert_eq!(_20, cast(20u64)); + assert_eq!(_20, cast(20i)); + assert_eq!(_20, cast(20i8)); + assert_eq!(_20, cast(20i16)); + assert_eq!(_20, cast(20i32)); + assert_eq!(_20, cast(20i64)); + assert_eq!(_20, cast(20f)); + assert_eq!(_20, cast(20f32)); + assert_eq!(_20, cast(20f64)); + }) +) + +#[test] fn test_u8_cast() { test_cast_20!(20u8) } +#[test] fn test_u16_cast() { test_cast_20!(20u16) } +#[test] fn test_u32_cast() { test_cast_20!(20u32) } +#[test] fn test_u64_cast() { test_cast_20!(20u64) } +#[test] fn test_uint_cast() { test_cast_20!(20u) } +#[test] fn test_i8_cast() { test_cast_20!(20i8) } +#[test] fn test_i16_cast() { test_cast_20!(20i16) } +#[test] fn test_i32_cast() { test_cast_20!(20i32) } +#[test] fn test_i64_cast() { test_cast_20!(20i64) } +#[test] fn test_int_cast() { test_cast_20!(20i) } +#[test] fn test_f32_cast() { test_cast_20!(20f32) } +#[test] fn test_f64_cast() { test_cast_20!(20f64) } +#[test] fn test_float_cast() { test_cast_20!(20f) } diff --git a/src/libstd/num/rational.rs b/src/libstd/num/rational.rs deleted file mode 100644 index d57c642c5a2..00000000000 --- a/src/libstd/num/rational.rs +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - - -//! Rational numbers - -use core::num::{Zero,One,ToStrRadix,FromStrRadix,Round}; -use core::from_str::FromStr; -use super::bigint::BigInt; - -/// Represents the ratio between 2 numbers. -#[deriving(Clone)] -pub struct Ratio<T> { - numer: T, - denom: T -} - -/// Alias for a `Ratio` of machine-sized integers. -pub type Rational = Ratio<int>; -pub type Rational32 = Ratio<i32>; -pub type Rational64 = Ratio<i64>; - -/// Alias for arbitrary precision rationals. -pub type BigRational = Ratio<BigInt>; - -impl<T: Clone + Integer + Ord> - Ratio<T> { - /// Create a ratio representing the integer `t`. - #[inline(always)] - pub fn from_integer(t: T) -> Ratio<T> { - Ratio::new_raw(t, One::one()) - } - - /// Create a ratio without checking for `denom == 0` or reducing. - #[inline(always)] - pub fn new_raw(numer: T, denom: T) -> Ratio<T> { - Ratio { numer: numer, denom: denom } - } - - // Create a new Ratio. Fails if `denom == 0`. - #[inline(always)] - pub fn new(numer: T, denom: T) -> Ratio<T> { - if denom == Zero::zero() { - fail!("denominator == 0"); - } - let mut ret = Ratio::new_raw(numer, denom); - ret.reduce(); - ret - } - - /// Put self into lowest terms, with denom > 0. - fn reduce(&mut self) { - let g : T = self.numer.gcd(&self.denom); - - // FIXME(#6050): overloaded operators force moves with generic types - // self.numer /= g; - self.numer = self.numer / g; - // FIXME(#6050): overloaded operators force moves with generic types - // self.denom /= g; - self.denom = self.denom / g; - - // keep denom positive! - if self.denom < Zero::zero() { - self.numer = -self.numer; - self.denom = -self.denom; - } - } - - /// Return a `reduce`d copy of self. - fn reduced(&self) -> Ratio<T> { - let mut ret = self.clone(); - ret.reduce(); - ret - } -} - -/* Comparisons */ - -// comparing a/b and c/d is the same as comparing a*d and b*c, so we -// abstract that pattern. The following macro takes a trait and either -// a comma-separated list of "method name -> return value" or just -// "method name" (return value is bool in that case) -macro_rules! cmp_impl { - (impl $imp:ident, $($method:ident),+) => { - cmp_impl!(impl $imp, $($method -> bool),+) - }; - // return something other than a Ratio<T> - (impl $imp:ident, $($method:ident -> $res:ty),+) => { - impl<T: Mul<T,T> + $imp> $imp for Ratio<T> { - $( - #[inline] - fn $method(&self, other: &Ratio<T>) -> $res { - (self.numer * other.denom). $method (&(self.denom*other.numer)) - } - )+ - } - }; -} -cmp_impl!(impl Eq, eq, ne) -cmp_impl!(impl TotalEq, equals) -cmp_impl!(impl Ord, lt, gt, le, ge) -cmp_impl!(impl TotalOrd, cmp -> cmp::Ordering) - -/* Arithmetic */ -// a/b * c/d = (a*c)/(b*d) -impl<T: Clone + Integer + Ord> - Mul<Ratio<T>,Ratio<T>> for Ratio<T> { - #[inline] - fn mul(&self, rhs: &Ratio<T>) -> Ratio<T> { - Ratio::new(self.numer * rhs.numer, self.denom * rhs.denom) - } -} - -// (a/b) / (c/d) = (a*d)/(b*c) -impl<T: Clone + Integer + Ord> - Div<Ratio<T>,Ratio<T>> for Ratio<T> { - #[inline] - fn div(&self, rhs: &Ratio<T>) -> Ratio<T> { - Ratio::new(self.numer * rhs.denom, self.denom * rhs.numer) - } -} - -// Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern -macro_rules! arith_impl { - (impl $imp:ident, $method:ident) => { - impl<T: Clone + Integer + Ord> - $imp<Ratio<T>,Ratio<T>> for Ratio<T> { - #[inline] - fn $method(&self, rhs: &Ratio<T>) -> Ratio<T> { - Ratio::new((self.numer * rhs.denom).$method(&(self.denom * rhs.numer)), - self.denom * rhs.denom) - } - } - } -} - -// a/b + c/d = (a*d + b*c)/(b*d -arith_impl!(impl Add, add) - -// a/b - c/d = (a*d - b*c)/(b*d) -arith_impl!(impl Sub, sub) - -// a/b % c/d = (a*d % b*c)/(b*d) -arith_impl!(impl Rem, rem) - -impl<T: Clone + Integer + Ord> - Neg<Ratio<T>> for Ratio<T> { - #[inline] - fn neg(&self) -> Ratio<T> { - Ratio::new_raw(-self.numer, self.denom.clone()) - } -} - -/* Constants */ -impl<T: Clone + Integer + Ord> - Zero for Ratio<T> { - #[inline] - fn zero() -> Ratio<T> { - Ratio::new_raw(Zero::zero(), One::one()) - } - - #[inline] - fn is_zero(&self) -> bool { - *self == Zero::zero() - } -} - -impl<T: Clone + Integer + Ord> - One for Ratio<T> { - #[inline] - fn one() -> Ratio<T> { - Ratio::new_raw(One::one(), One::one()) - } -} - -impl<T: Clone + Integer + Ord> - Num for Ratio<T> {} - -/* Utils */ -impl<T: Clone + Integer + Ord> - Round for Ratio<T> { - - fn floor(&self) -> Ratio<T> { - if *self < Zero::zero() { - Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom) - } else { - Ratio::from_integer(self.numer / self.denom) - } - } - - fn ceil(&self) -> Ratio<T> { - if *self < Zero::zero() { - Ratio::from_integer(self.numer / self.denom) - } else { - Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom) - } - } - - #[inline(always)] - fn round(&self) -> Ratio<T> { - if *self < Zero::zero() { - Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom) - } else { - Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom) - } - } - - #[inline(always)] - fn trunc(&self) -> Ratio<T> { - Ratio::from_integer(self.numer / self.denom) - } - - fn fract(&self) -> Ratio<T> { - Ratio::new_raw(self.numer % self.denom, self.denom.clone()) - } -} - -impl<T: Clone + Integer + Ord> Fractional for Ratio<T> { - #[inline] - fn recip(&self) -> Ratio<T> { - Ratio::new_raw(self.denom.clone(), self.numer.clone()) - } -} - -/* String conversions */ -impl<T: ToStr> ToStr for Ratio<T> { - /// Renders as `numer/denom`. - fn to_str(&self) -> ~str { - fmt!("%s/%s", self.numer.to_str(), self.denom.to_str()) - } -} -impl<T: ToStrRadix> ToStrRadix for Ratio<T> { - /// Renders as `numer/denom` where the numbers are in base `radix`. - fn to_str_radix(&self, radix: uint) -> ~str { - fmt!("%s/%s", self.numer.to_str_radix(radix), self.denom.to_str_radix(radix)) - } -} - -impl<T: FromStr + Clone + Integer + Ord> - FromStr for Ratio<T> { - /// Parses `numer/denom`. - fn from_str(s: &str) -> Option<Ratio<T>> { - let split = vec::build(|push| { - for str::each_splitn_char(s, '/', 1) |s| { - push(s.to_owned()); - } - }); - if split.len() < 2 { return None; } - do FromStr::from_str::<T>(split[0]).chain |a| { - do FromStr::from_str::<T>(split[1]).chain |b| { - Some(Ratio::new(a.clone(), b.clone())) - } - } - } -} -impl<T: FromStrRadix + Clone + Integer + Ord> - FromStrRadix for Ratio<T> { - /// Parses `numer/denom` where the numbers are in base `radix`. - fn from_str_radix(s: &str, radix: uint) -> Option<Ratio<T>> { - let split = vec::build(|push| { - for str::each_splitn_char(s, '/', 1) |s| { - push(s.to_owned()); - } - }); - if split.len() < 2 { None } - else { - do FromStrRadix::from_str_radix::<T>(split[0], radix).chain |a| { - do FromStrRadix::from_str_radix::<T>(split[1], radix).chain |b| { - Some(Ratio::new(a.clone(), b.clone())) - } - } - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use core::num::{Zero,One,FromStrRadix,IntConvertible}; - use core::from_str::FromStr; - - pub static _0 : Rational = Ratio { numer: 0, denom: 1}; - pub static _1 : Rational = Ratio { numer: 1, denom: 1}; - pub static _2: Rational = Ratio { numer: 2, denom: 1}; - pub static _1_2: Rational = Ratio { numer: 1, denom: 2}; - pub static _3_2: Rational = Ratio { numer: 3, denom: 2}; - pub static _neg1_2: Rational = Ratio { numer: -1, denom: 2}; - - pub fn to_big(n: Rational) -> BigRational { - Ratio::new( - IntConvertible::from_int(n.numer), - IntConvertible::from_int(n.denom) - ) - } - - #[test] - fn test_test_constants() { - // check our constants are what Ratio::new etc. would make. - assert_eq!(_0, Zero::zero()); - assert_eq!(_1, One::one()); - assert_eq!(_2, Ratio::from_integer(2)); - assert_eq!(_1_2, Ratio::new(1,2)); - assert_eq!(_3_2, Ratio::new(3,2)); - assert_eq!(_neg1_2, Ratio::new(-1,2)); - } - - #[test] - fn test_new_reduce() { - let one22 = Ratio::new(2i,2); - - assert_eq!(one22, One::one()); - } - #[test] - #[should_fail] - fn test_new_zero() { - let _a = Ratio::new(1,0); - } - - - #[test] - fn test_cmp() { - assert!(_0 == _0 && _1 == _1); - assert!(_0 != _1 && _1 != _0); - assert!(_0 < _1 && !(_1 < _0)); - assert!(_1 > _0 && !(_0 > _1)); - - assert!(_0 <= _0 && _1 <= _1); - assert!(_0 <= _1 && !(_1 <= _0)); - - assert!(_0 >= _0 && _1 >= _1); - assert!(_1 >= _0 && !(_0 >= _1)); - } - - - mod arith { - use super::*; - use super::super::*; - - - #[test] - fn test_add() { - fn test(a: Rational, b: Rational, c: Rational) { - assert_eq!(a + b, c); - assert_eq!(to_big(a) + to_big(b), to_big(c)); - } - - test(_1, _1_2, _3_2); - test(_1, _1, _2); - test(_1_2, _3_2, _2); - test(_1_2, _neg1_2, _0); - } - - #[test] - fn test_sub() { - fn test(a: Rational, b: Rational, c: Rational) { - assert_eq!(a - b, c); - assert_eq!(to_big(a) - to_big(b), to_big(c)) - } - - test(_1, _1_2, _1_2); - test(_3_2, _1_2, _1); - test(_1, _neg1_2, _3_2); - } - - #[test] - fn test_mul() { - fn test(a: Rational, b: Rational, c: Rational) { - assert_eq!(a * b, c); - assert_eq!(to_big(a) * to_big(b), to_big(c)) - } - - test(_1, _1_2, _1_2); - test(_1_2, _3_2, Ratio::new(3,4)); - test(_1_2, _neg1_2, Ratio::new(-1, 4)); - } - - #[test] - fn test_div() { - fn test(a: Rational, b: Rational, c: Rational) { - assert_eq!(a / b, c); - assert_eq!(to_big(a) / to_big(b), to_big(c)) - } - - test(_1, _1_2, _2); - test(_3_2, _1_2, _1 + _2); - test(_1, _neg1_2, _neg1_2 + _neg1_2 + _neg1_2 + _neg1_2); - } - - #[test] - fn test_rem() { - fn test(a: Rational, b: Rational, c: Rational) { - assert_eq!(a % b, c); - assert_eq!(to_big(a) % to_big(b), to_big(c)) - } - - test(_3_2, _1, _1_2); - test(_2, _neg1_2, _0); - test(_1_2, _2, _1_2); - } - - #[test] - fn test_neg() { - fn test(a: Rational, b: Rational) { - assert_eq!(-a, b); - assert_eq!(-to_big(a), to_big(b)) - } - - test(_0, _0); - test(_1_2, _neg1_2); - test(-_1, _1); - } - #[test] - fn test_zero() { - assert_eq!(_0 + _0, _0); - assert_eq!(_0 * _0, _0); - assert_eq!(_0 * _1, _0); - assert_eq!(_0 / _neg1_2, _0); - assert_eq!(_0 - _0, _0); - } - #[test] - #[should_fail] - fn test_div_0() { - let _a = _1 / _0; - } - } - - #[test] - fn test_round() { - assert_eq!(_1_2.ceil(), _1); - assert_eq!(_1_2.floor(), _0); - assert_eq!(_1_2.round(), _1); - assert_eq!(_1_2.trunc(), _0); - - assert_eq!(_neg1_2.ceil(), _0); - assert_eq!(_neg1_2.floor(), -_1); - assert_eq!(_neg1_2.round(), -_1); - assert_eq!(_neg1_2.trunc(), _0); - - assert_eq!(_1.ceil(), _1); - assert_eq!(_1.floor(), _1); - assert_eq!(_1.round(), _1); - assert_eq!(_1.trunc(), _1); - } - - #[test] - fn test_fract() { - assert_eq!(_1.fract(), _0); - assert_eq!(_neg1_2.fract(), _neg1_2); - assert_eq!(_1_2.fract(), _1_2); - assert_eq!(_3_2.fract(), _1_2); - } - - #[test] - fn test_recip() { - assert_eq!(_1 * _1.recip(), _1); - assert_eq!(_2 * _2.recip(), _1); - assert_eq!(_1_2 * _1_2.recip(), _1); - assert_eq!(_3_2 * _3_2.recip(), _1); - assert_eq!(_neg1_2 * _neg1_2.recip(), _1); - } - - #[test] - fn test_to_from_str() { - fn test(r: Rational, s: ~str) { - assert_eq!(FromStr::from_str(s), Some(r)); - assert_eq!(r.to_str(), s); - } - test(_1, ~"1/1"); - test(_0, ~"0/1"); - test(_1_2, ~"1/2"); - test(_3_2, ~"3/2"); - test(_2, ~"2/1"); - test(_neg1_2, ~"-1/2"); - } - #[test] - fn test_from_str_fail() { - fn test(s: &str) { - assert_eq!(FromStr::from_str::<Rational>(s), None); - } - - for ["0 /1", "abc", "", "1/", "--1/2","3/2/1"].each |&s| { - test(s); - } - } - - #[test] - fn test_to_from_str_radix() { - fn test(r: Rational, s: ~str, n: uint) { - assert_eq!(FromStrRadix::from_str_radix(s, n), Some(r)); - assert_eq!(r.to_str_radix(n), s); - } - fn test3(r: Rational, s: ~str) { test(r, s, 3) } - fn test16(r: Rational, s: ~str) { test(r, s, 16) } - - test3(_1, ~"1/1"); - test3(_0, ~"0/1"); - test3(_1_2, ~"1/2"); - test3(_3_2, ~"10/2"); - test3(_2, ~"2/1"); - test3(_neg1_2, ~"-1/2"); - test3(_neg1_2 / _2, ~"-1/11"); - - test16(_1, ~"1/1"); - test16(_0, ~"0/1"); - test16(_1_2, ~"1/2"); - test16(_3_2, ~"3/2"); - test16(_2, ~"2/1"); - test16(_neg1_2, ~"-1/2"); - test16(_neg1_2 / _2, ~"-1/4"); - test16(Ratio::new(13,15), ~"d/f"); - test16(_1_2*_1_2*_1_2*_1_2, ~"1/10"); - } - - #[test] - fn test_from_str_radix_fail() { - fn test(s: &str) { - assert_eq!(FromStrRadix::from_str_radix::<Rational>(s, 3), None); - } - - for ["0 /1", "abc", "", "1/", "--1/2","3/2/1", "3/2"].each |&s| { - test(s); - } - } -} diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs new file mode 100644 index 00000000000..1d65b84b7ce --- /dev/null +++ b/src/libstd/num/strconv.rs @@ -0,0 +1,673 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 container::Container; +use core::cmp::{Ord, Eq}; +use ops::{Add, Sub, Mul, Div, Rem, Neg}; +use option::{None, Option, Some}; +use char; +use str; +use kinds::Copy; +use vec; +use vec::{CopyableVector, ImmutableVector}; +use vec::OwnedVector; +use num::{NumCast, Zero, One, cast, pow_with_uint}; +use f64; + +pub enum ExponentFormat { + ExpNone, + ExpDec, + ExpBin +} + +pub enum SignificantDigits { + DigAll, + DigMax(uint), + DigExact(uint) +} + +pub enum SignFormat { + SignNone, + SignNeg, + SignAll +} + +#[inline(always)] +fn is_NaN<T:Eq>(num: &T) -> bool { + *num != *num +} + +#[inline(always)] +fn is_inf<T:Eq+NumStrConv>(num: &T) -> bool { + match NumStrConv::inf() { + None => false, + Some(n) => *num == n + } +} + +#[inline(always)] +fn is_neg_inf<T:Eq+NumStrConv>(num: &T) -> bool { + match NumStrConv::neg_inf() { + None => false, + Some(n) => *num == n + } +} + +#[inline(always)] +fn is_neg_zero<T:Eq+One+Zero+NumStrConv+Div<T,T>>(num: &T) -> bool { + let _0: T = Zero::zero(); + let _1: T = One::one(); + + *num == _0 && is_neg_inf(&(_1 / *num)) +} + +pub trait NumStrConv { + fn NaN() -> Option<Self>; + fn inf() -> Option<Self>; + fn neg_inf() -> Option<Self>; + fn neg_zero() -> Option<Self>; + + fn round_to_zero(&self) -> Self; + fn fractional_part(&self) -> Self; +} + +macro_rules! impl_NumStrConv_Floating (($t:ty) => ( + impl NumStrConv for $t { + #[inline(always)] + fn NaN() -> Option<$t> { Some( 0.0 / 0.0) } + #[inline(always)] + fn inf() -> Option<$t> { Some( 1.0 / 0.0) } + #[inline(always)] + fn neg_inf() -> Option<$t> { Some(-1.0 / 0.0) } + #[inline(always)] + fn neg_zero() -> Option<$t> { Some(-0.0 ) } + + #[inline(always)] + fn round_to_zero(&self) -> $t { + ( if *self < 0.0 { f64::ceil(*self as f64) } + else { f64::floor(*self as f64) } + ) as $t + } + + #[inline(always)] + fn fractional_part(&self) -> $t { + *self - self.round_to_zero() + } + } +)) + +macro_rules! impl_NumStrConv_Integer (($t:ty) => ( + impl NumStrConv for $t { + #[inline(always)] fn NaN() -> Option<$t> { None } + #[inline(always)] fn inf() -> Option<$t> { None } + #[inline(always)] fn neg_inf() -> Option<$t> { None } + #[inline(always)] fn neg_zero() -> Option<$t> { None } + + #[inline(always)] fn round_to_zero(&self) -> $t { *self } + #[inline(always)] fn fractional_part(&self) -> $t { 0 } + } +)) + +// FIXME: #4955 +// Replace by two generic impls for traits 'Integral' and 'Floating' +impl_NumStrConv_Floating!(float) +impl_NumStrConv_Floating!(f32) +impl_NumStrConv_Floating!(f64) + +impl_NumStrConv_Integer!(int) +impl_NumStrConv_Integer!(i8) +impl_NumStrConv_Integer!(i16) +impl_NumStrConv_Integer!(i32) +impl_NumStrConv_Integer!(i64) + +impl_NumStrConv_Integer!(uint) +impl_NumStrConv_Integer!(u8) +impl_NumStrConv_Integer!(u16) +impl_NumStrConv_Integer!(u32) +impl_NumStrConv_Integer!(u64) + + +// Special value strings as [u8] consts. +static inf_buf: [u8, ..3] = ['i' as u8, 'n' as u8, 'f' as u8]; +static positive_inf_buf: [u8, ..4] = ['+' as u8, 'i' as u8, 'n' as u8, + 'f' as u8]; +static negative_inf_buf: [u8, ..4] = ['-' as u8, 'i' as u8, 'n' as u8, + 'f' as u8]; +static nan_buf: [u8, ..3] = ['N' as u8, 'a' as u8, 'N' as u8]; + +/** + * Converts a number to its string representation as a byte vector. + * This is meant to be a common base implementation for all numeric string + * conversion functions like `to_str()` or `to_str_radix()`. + * + * # Arguments + * - `num` - The number to convert. Accepts any number that + * implements the numeric traits. + * - `radix` - Base to use. Accepts only the values 2-36. + * - `negative_zero` - Whether to treat the special value `-0` as + * `-0` or as `+0`. + * - `sign` - How to emit the sign. Options are: + * - `SignNone`: No sign at all. Basically emits `abs(num)`. + * - `SignNeg`: Only `-` on negative values. + * - `SignAll`: Both `+` on positive, and `-` on negative numbers. + * - `digits` - The amount of digits to use for emitting the + * fractional part, if any. Options are: + * - `DigAll`: All calculatable digits. Beware of bignums or + * fractions! + * - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros. + * - `DigExact(uint)`: Exactly N digits. + * + * # Return value + * A tuple containing the byte vector, and a boolean flag indicating + * whether it represents a special value like `inf`, `-inf`, `NaN` or not. + * It returns a tuple because there can be ambiguity between a special value + * and a number representation at higher bases. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + */ +pub fn to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+NumStrConv+Copy+ + Div<T,T>+Neg<T>+Rem<T,T>+Mul<T,T>>( + num: &T, radix: uint, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { + if (radix as int) < 2 { + fail!("to_str_bytes_common: radix %? to low, must lie in the range [2, 36]", radix); + } else if radix as int > 36 { + fail!("to_str_bytes_common: radix %? to high, must lie in the range [2, 36]", radix); + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if is_NaN(num) { + return (str::to_bytes("NaN"), true); + } + else if is_inf(num){ + return match sign { + SignAll => (str::to_bytes("+inf"), true), + _ => (str::to_bytes("inf"), true) + } + } + else if is_neg_inf(num) { + return match sign { + SignNone => (str::to_bytes("inf"), true), + _ => (str::to_bytes("-inf"), true), + } + } + + let neg = *num < _0 || (negative_zero && is_neg_zero(num)); + let mut buf: ~[u8] = ~[]; + let radix_gen: T = cast(radix as int); + + let mut deccum; + + // First emit the non-fractional part, looping at least once to make + // sure at least a `0` gets emitted. + deccum = num.round_to_zero(); + loop { + // Calculate the absolute value of each digit instead of only + // doing it once for the whole number because a + // representable negative number doesn't necessary have an + // representable additive inverse of the same type + // (See twos complement). But we assume that for the + // numbers [-35 .. 0] we always have [0 .. 35]. + let current_digit_signed = deccum % radix_gen; + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + // Decrease the deccumulator one digit at a time + deccum /= radix_gen; + deccum = deccum.round_to_zero(); + + buf.push(char::from_digit(current_digit.to_int() as uint, radix) + .unwrap() as u8); + + // No more digits to calculate for the non-fractional part -> break + if deccum == _0 { break; } + } + + // If limited digits, calculate one digit more for rounding. + let (limit_digits, digit_count, exact) = match digits { + DigAll => (false, 0u, false), + DigMax(count) => (true, count+1, false), + DigExact(count) => (true, count+1, true) + }; + + // Decide what sign to put in front + match sign { + SignNeg | SignAll if neg => { + buf.push('-' as u8); + } + SignAll => { + buf.push('+' as u8); + } + _ => () + } + + vec::reverse(buf); + + // Remember start of the fractional digits. + // Points one beyond end of buf if none get generated, + // or at the '.' otherwise. + let start_fractional_digits = buf.len(); + + // Now emit the fractional part, if any + deccum = num.fractional_part(); + if deccum != _0 || (limit_digits && exact && digit_count > 0) { + buf.push('.' as u8); + let mut dig = 0u; + + // calculate new digits while + // - there is no limit and there are digits left + // - or there is a limit, it's not reached yet and + // - it's exact + // - or it's a maximum, and there are still digits left + while (!limit_digits && deccum != _0) + || (limit_digits && dig < digit_count && ( + exact + || (!exact && deccum != _0) + ) + ) { + // Shift first fractional digit into the integer part + deccum *= radix_gen; + + // Calculate the absolute value of each digit. + // See note in first loop. + let current_digit_signed = deccum.round_to_zero(); + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + buf.push(char::from_digit( + current_digit.to_int() as uint, radix).unwrap() as u8); + + // Decrease the deccumulator one fractional digit at a time + deccum = deccum.fractional_part(); + dig += 1u; + } + + // If digits are limited, and that limit has been reached, + // cut off the one extra digit, and depending on its value + // round the remaining ones. + if limit_digits && dig == digit_count { + let ascii2value = |chr: u8| { + char::to_digit(chr as char, radix).unwrap() as uint + }; + let value2ascii = |val: uint| { + char::from_digit(val, radix).unwrap() as u8 + }; + + let extra_digit = ascii2value(buf.pop()); + if extra_digit >= radix / 2 { // -> need to round + let mut i: int = buf.len() as int - 1; + loop { + // If reached left end of number, have to + // insert additional digit: + if i < 0 + || buf[i] == '-' as u8 + || buf[i] == '+' as u8 { + buf.insert((i + 1) as uint, value2ascii(1)); + break; + } + + // Skip the '.' + if buf[i] == '.' as u8 { i -= 1; loop; } + + // Either increment the digit, + // or set to 0 if max and carry the 1. + let current_digit = ascii2value(buf[i]); + if current_digit < (radix - 1) { + buf[i] = value2ascii(current_digit+1); + break; + } else { + buf[i] = value2ascii(0); + i -= 1; + } + } + } + } + } + + // if number of digits is not exact, remove all trailing '0's up to + // and including the '.' + if !exact { + let buf_max_i = buf.len() - 1; + + // index to truncate from + let mut i = buf_max_i; + + // discover trailing zeros of fractional part + while i > start_fractional_digits && buf[i] == '0' as u8 { + i -= 1; + } + + // Only attempt to truncate digits if buf has fractional digits + if i >= start_fractional_digits { + // If buf ends with '.', cut that too. + if buf[i] == '.' as u8 { i -= 1 } + + // only resize buf if we actually remove digits + if i < buf_max_i { + buf = buf.slice(0, i + 1).to_owned(); + } + } + } // If exact and trailing '.', just cut that + else { + let max_i = buf.len() - 1; + if buf[max_i] == '.' as u8 { + buf = buf.slice(0, max_i).to_owned(); + } + } + + (buf, false) +} + +/** + * Converts a number to its string representation. This is a wrapper for + * `to_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub fn to_str_common<T:NumCast+Zero+One+Eq+Ord+NumStrConv+Copy+ + Div<T,T>+Neg<T>+Rem<T,T>+Mul<T,T>>( + num: &T, radix: uint, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { + let (bytes, special) = to_str_bytes_common(num, radix, + negative_zero, sign, digits); + (str::from_bytes(bytes), special) +} + +// Some constants for from_str_bytes_common's input validation, +// they define minimum radix values for which the character is a valid digit. +priv static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; +priv static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; +priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; + +/** + * Parses a byte slice as a number. This is meant to + * be a common base implementation for all numeric string conversion + * functions like `from_str()` or `from_str_radix()`. + * + * # Arguments + * - `buf` - The byte slice to parse. + * - `radix` - Which base to parse the number as. Accepts 2-36. + * - `negative` - Whether to accept negative numbers. + * - `fractional` - Whether to accept numbers with fractional parts. + * - `special` - Whether to accept special values like `inf` + * and `NaN`. Can conflict with `radix`, see Failure. + * - `exponent` - Which exponent format to accept. Options are: + * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or + * `-8.2`. + * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or + * `8.2E-2`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or + * `FFp128`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `empty_zero` - Whether to accept a empty `buf` as a 0 or not. + * - `ignore_underscores` - Whether all underscores within the string should + * be ignored. + * + * # Return value + * Returns `Some(n)` if `buf` parses to a number n without overflowing, and + * `None` otherwise, depending on the constraints set by the remaining + * arguments. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict + * between digit and exponent sign `'e'`. + * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict + * between digit and exponent sign `'p'`. + * - Fails if `radix` > 18 and `special == true` due to conflict + * between digit and lowest first character in `inf` and `NaN`, the `'i'`. + */ +pub fn from_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Copy+Div<T,T>+ + Mul<T,T>+Sub<T,T>+Neg<T>+Add<T,T>+ + NumStrConv>( + buf: &[u8], radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool, + ignore_underscores: bool + ) -> Option<T> { + match exponent { + ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' + => fail!("from_str_bytes_common: radix %? incompatible with \ + use of 'e' as decimal exponent", radix), + ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' + => fail!("from_str_bytes_common: radix %? incompatible with \ + use of 'p' as binary exponent", radix), + _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' + => fail!("from_str_bytes_common: radix %? incompatible with \ + special values 'inf' and 'NaN'", radix), + _ if (radix as int) < 2 + => fail!("from_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix), + _ if (radix as int) > 36 + => fail!("from_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix), + _ => () + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + let radix_gen: T = cast(radix as int); + + let len = buf.len(); + + if len == 0 { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + if special { + if buf == inf_buf || buf == positive_inf_buf { + return NumStrConv::inf(); + } else if buf == negative_inf_buf { + if negative { + return NumStrConv::neg_inf(); + } else { + return None; + } + } else if buf == nan_buf { + return NumStrConv::NaN(); + } + } + + let (start, accum_positive) = match buf[0] as char { + '-' if !negative => return None, + '-' => (1u, false), + '+' => (1u, true), + _ => (0u, true) + }; + + // Initialize accumulator with signed zero for floating point parsing to + // work + let mut accum = if accum_positive { _0 } else { -_1 * _0}; + let mut last_accum = accum; // Necessary to detect overflow + let mut i = start; + let mut exp_found = false; + + // Parse integer part of number + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // shift accum one digit left + accum *= radix_gen; + + // add/subtract current digit depending on sign + if accum_positive { + accum += cast(digit as int); + } else { + accum -= cast(digit as int); + } + + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if last_accum != _0 { + if accum_positive && accum <= last_accum { return None; } + if !accum_positive && accum >= last_accum { return None; } + } + last_accum = accum; + } + None => match c { + '_' if ignore_underscores => {} + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + '.' if fractional => { + i += 1u; // skip the '.' + break; // start of fractional part + } + _ => return None // invalid number + } + } + + i += 1u; + } + + // Parse fractional part of number + // Skip if already reached start of exponent + if !exp_found { + let mut power = _1; + + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // Decrease power one order of magnitude + power /= radix_gen; + + let digit_t: T = cast(digit); + + // add/subtract current digit depending on sign + if accum_positive { + accum += digit_t * power; + } else { + accum -= digit_t * power; + } + + // Detect overflow by comparing to last value + if accum_positive && accum < last_accum { return None; } + if !accum_positive && accum > last_accum { return None; } + last_accum = accum; + } + None => match c { + '_' if ignore_underscores => {} + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + _ => return None // invalid number + } + } + + i += 1u; + } + } + + // Special case: buf not empty, but does not contain any digit in front + // of the exponent sign -> number is empty string + if i == start { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + let mut multiplier = _1; + + if exp_found { + let c = buf[i] as char; + let base = match (c, exponent) { + // c is never _ so don't need to handle specially + ('e', ExpDec) | ('E', ExpDec) => 10u, + ('p', ExpBin) | ('P', ExpBin) => 2u, + _ => return None // char doesn't fit given exponent format + }; + + // parse remaining bytes as decimal integer, + // skipping the exponent char + let exp: Option<int> = from_str_bytes_common( + buf.slice(i+1, len), 10, true, false, false, ExpNone, false, + ignore_underscores); + + match exp { + Some(exp_pow) => { + multiplier = if exp_pow < 0 { + _1 / pow_with_uint::<T>(base, (-exp_pow.to_int()) as uint) + } else { + pow_with_uint::<T>(base, exp_pow.to_int() as uint) + } + } + None => return None // invalid exponent -> invalid number + } + } + + Some(accum * multiplier) +} + +/** + * Parses a string as a number. This is a wrapper for + * `from_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub fn from_str_common<T:NumCast+Zero+One+Eq+Ord+Copy+Div<T,T>+Mul<T,T>+ + Sub<T,T>+Neg<T>+Add<T,T>+NumStrConv>( + buf: &str, radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool, + ignore_underscores: bool + ) -> Option<T> { + from_str_bytes_common(str::to_bytes(buf), radix, negative, + fractional, special, exponent, empty_zero, + ignore_underscores) +} + +#[cfg(test)] +mod test { + use super::*; + use option::*; + + #[test] + fn from_str_ignore_underscores() { + let s : Option<u8> = from_str_common("__1__", 2, false, false, false, + ExpNone, false, true); + assert_eq!(s, Some(1u8)); + + let n : Option<u8> = from_str_common("__1__", 2, false, false, false, + ExpNone, false, false); + assert_eq!(n, None); + + let f : Option<f32> = from_str_common("_1_._5_e_1_", 10, false, true, false, + ExpDec, false, true); + assert_eq!(f, Some(1.5e1f32)); + } + + #[test] + fn from_str_issue5770() { + // try to parse 0b1_1111_1111 = 511 as a u8. Caused problems + // since 255*2+1 == 255 (mod 256) so the overflow wasn't + // detected. + let n : Option<u8> = from_str_common("111111111", 2, false, false, false, + ExpNone, false, false); + assert_eq!(n, None); + } +} diff --git a/src/libstd/num/uint-template.rs b/src/libstd/num/uint-template.rs new file mode 100644 index 00000000000..66ff16cbeb1 --- /dev/null +++ b/src/libstd/num/uint-template.rs @@ -0,0 +1,641 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 T = self::inst::T; +use T_SIGNED = self::inst::T_SIGNED; + +use num::{ToStrRadix, FromStrRadix}; +use num::{Zero, One, strconv}; +use prelude::*; + +pub use cmp::{min, max}; + +pub static bits : uint = inst::bits; +pub static bytes : uint = (inst::bits / 8); + +pub static min_value: T = 0 as T; +pub static max_value: T = 0 as T - 1 as T; + +#[inline(always)] +pub fn add(x: T, y: T) -> T { x + y } +#[inline(always)] +pub fn sub(x: T, y: T) -> T { x - y } +#[inline(always)] +pub fn mul(x: T, y: T) -> T { x * y } +#[inline(always)] +pub fn div(x: T, y: T) -> T { x / y } +#[inline(always)] +pub fn rem(x: T, y: T) -> T { x % y } + +#[inline(always)] +pub fn lt(x: T, y: T) -> bool { x < y } +#[inline(always)] +pub fn le(x: T, y: T) -> bool { x <= y } +#[inline(always)] +pub fn eq(x: T, y: T) -> bool { x == y } +#[inline(always)] +pub fn ne(x: T, y: T) -> bool { x != y } +#[inline(always)] +pub fn ge(x: T, y: T) -> bool { x >= y } +#[inline(always)] +pub fn gt(x: T, y: T) -> bool { x > y } + +#[inline(always)] +/// +/// Iterate over the range [`start`,`start`+`step`..`stop`) +/// +pub fn _range_step(start: T, + stop: T, + step: T_SIGNED, + it: &fn(T) -> bool) -> bool { + let mut i = start; + if step == 0 { + fail!("range_step called with step == 0"); + } + if step >= 0 { + while i < stop { + if !it(i) { return false; } + // avoiding overflow. break if i + step > max_value + if i > max_value - (step as T) { return true; } + i += step as T; + } + } else { + while i > stop { + if !it(i) { return false; } + // avoiding underflow. break if i + step < min_value + if i < min_value + ((-step) as T) { return true; } + i -= -step as T; + } + } + return true; +} + +pub fn range_step(start: T, stop: T, step: T_SIGNED, it: &fn(T) -> bool) -> bool { + _range_step(start, stop, step, it) +} + +#[inline(always)] +/// Iterate over the range [`lo`..`hi`) +pub fn range(lo: T, hi: T, it: &fn(T) -> bool) -> bool { + range_step(lo, hi, 1 as T_SIGNED, it) +} + +#[inline(always)] +/// Iterate over the range [`hi`..`lo`) +pub fn range_rev(hi: T, lo: T, it: &fn(T) -> bool) -> bool { + range_step(hi, lo, -1 as T_SIGNED, it) +} + +/// Computes the bitwise complement +#[inline(always)] +pub fn compl(i: T) -> T { + max_value ^ i +} + +impl Num for T {} + +#[cfg(not(test))] +impl Ord for T { + #[inline(always)] + fn lt(&self, other: &T) -> bool { (*self) < (*other) } + #[inline(always)] + fn le(&self, other: &T) -> bool { (*self) <= (*other) } + #[inline(always)] + fn ge(&self, other: &T) -> bool { (*self) >= (*other) } + #[inline(always)] + fn gt(&self, other: &T) -> bool { (*self) > (*other) } +} + +#[cfg(not(test))] +impl Eq for T { + #[inline(always)] + fn eq(&self, other: &T) -> bool { return (*self) == (*other); } + #[inline(always)] + fn ne(&self, other: &T) -> bool { return (*self) != (*other); } +} + +impl Orderable for T { + #[inline(always)] + fn min(&self, other: &T) -> T { + if *self < *other { *self } else { *other } + } + + #[inline(always)] + fn max(&self, other: &T) -> T { + if *self > *other { *self } else { *other } + } + + /// Returns the number constrained within the range `mn <= self <= mx`. + #[inline(always)] + fn clamp(&self, mn: &T, mx: &T) -> T { + cond!( + (*self > *mx) { *mx } + (*self < *mn) { *mn } + _ { *self } + ) + } +} + +impl Zero for T { + #[inline(always)] + fn zero() -> T { 0 } + + #[inline(always)] + fn is_zero(&self) -> bool { *self == 0 } +} + +impl One for T { + #[inline(always)] + fn one() -> T { 1 } +} + +#[cfg(not(test))] +impl Add<T,T> for T { + #[inline(always)] + fn add(&self, other: &T) -> T { *self + *other } +} + +#[cfg(not(test))] +impl Sub<T,T> for T { + #[inline(always)] + fn sub(&self, other: &T) -> T { *self - *other } +} + +#[cfg(not(test))] +impl Mul<T,T> for T { + #[inline(always)] + fn mul(&self, other: &T) -> T { *self * *other } +} + +#[cfg(not(test))] +impl Div<T,T> for T { + #[inline(always)] + fn div(&self, other: &T) -> T { *self / *other } +} + +#[cfg(not(test))] +impl Rem<T,T> for T { + #[inline(always)] + fn rem(&self, other: &T) -> T { *self % *other } +} + +#[cfg(not(test))] +impl Neg<T> for T { + #[inline(always)] + fn neg(&self) -> T { -*self } +} + +impl Unsigned for T {} + +impl Integer for T { + /// Calculates `div` (`\`) and `rem` (`%`) simultaneously + #[inline(always)] + fn div_rem(&self, other: &T) -> (T,T) { + (*self / *other, *self % *other) + } + + /// Unsigned integer division. Returns the same result as `div` (`/`). + #[inline(always)] + fn div_floor(&self, other: &T) -> T { *self / *other } + + /// Unsigned integer modulo operation. Returns the same result as `rem` (`%`). + #[inline(always)] + fn mod_floor(&self, other: &T) -> T { *self / *other } + + /// Calculates `div_floor` and `modulo_floor` simultaneously + #[inline(always)] + fn div_mod_floor(&self, other: &T) -> (T,T) { + (*self / *other, *self % *other) + } + + /// Calculates the Greatest Common Divisor (GCD) of the number and `other` + #[inline(always)] + fn gcd(&self, other: &T) -> T { + // Use Euclid's algorithm + let mut m = *self, n = *other; + while m != 0 { + let temp = m; + m = n % temp; + n = temp; + } + n + } + + /// Calculates the Lowest Common Multiple (LCM) of the number and `other` + #[inline(always)] + fn lcm(&self, other: &T) -> T { + (*self * *other) / self.gcd(other) + } + + /// Returns `true` if the number can be divided by `other` without leaving a remainder + #[inline(always)] + fn is_multiple_of(&self, other: &T) -> bool { *self % *other == 0 } + + /// Returns `true` if the number is divisible by `2` + #[inline(always)] + fn is_even(&self) -> bool { self.is_multiple_of(&2) } + + /// Returns `true` if the number is not divisible by `2` + #[inline(always)] + fn is_odd(&self) -> bool { !self.is_even() } +} + +impl Bitwise for T {} + +#[cfg(not(test))] +impl BitOr<T,T> for T { + #[inline(always)] + fn bitor(&self, other: &T) -> T { *self | *other } +} + +#[cfg(not(test))] +impl BitAnd<T,T> for T { + #[inline(always)] + fn bitand(&self, other: &T) -> T { *self & *other } +} + +#[cfg(not(test))] +impl BitXor<T,T> for T { + #[inline(always)] + fn bitxor(&self, other: &T) -> T { *self ^ *other } +} + +#[cfg(not(test))] +impl Shl<T,T> for T { + #[inline(always)] + fn shl(&self, other: &T) -> T { *self << *other } +} + +#[cfg(not(test))] +impl Shr<T,T> for T { + #[inline(always)] + fn shr(&self, other: &T) -> T { *self >> *other } +} + +#[cfg(not(test))] +impl Not<T> for T { + #[inline(always)] + fn not(&self) -> T { !*self } +} + +impl Bounded for T { + #[inline(always)] + fn min_value() -> T { min_value } + + #[inline(always)] + fn max_value() -> T { max_value } +} + +impl Int for T {} + +// String conversion functions and impl str -> num + +/// Parse a string as a number in base 10. +#[inline(always)] +pub fn from_str(s: &str) -> Option<T> { + strconv::from_str_common(s, 10u, false, false, false, + strconv::ExpNone, false, false) +} + +/// Parse a string as a number in the given base. +#[inline(always)] +pub fn from_str_radix(s: &str, radix: uint) -> Option<T> { + strconv::from_str_common(s, radix, false, false, false, + strconv::ExpNone, false, false) +} + +/// Parse a byte slice as a number in the given base. +#[inline(always)] +pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> { + strconv::from_str_bytes_common(buf, radix, false, false, false, + strconv::ExpNone, false, false) +} + +impl FromStr for T { + #[inline(always)] + fn from_str(s: &str) -> Option<T> { + from_str(s) + } +} + +impl FromStrRadix for T { + #[inline(always)] + fn from_str_radix(s: &str, radix: uint) -> Option<T> { + from_str_radix(s, radix) + } +} + +// String conversion functions and impl num -> str + +/// Convert to a string as a byte slice in a given base. +#[inline(always)] +pub fn to_str_bytes<U>(n: T, radix: uint, f: &fn(v: &[u8]) -> U) -> U { + let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, + strconv::SignNeg, strconv::DigAll); + f(buf) +} + +/// Convert to a string in base 10. +#[inline(always)] +pub fn to_str(num: T) -> ~str { + let (buf, _) = strconv::to_str_common(&num, 10u, false, + strconv::SignNeg, strconv::DigAll); + buf +} + +/// Convert to a string in a given base. +#[inline(always)] +pub fn to_str_radix(num: T, radix: uint) -> ~str { + let (buf, _) = strconv::to_str_common(&num, radix, false, + strconv::SignNeg, strconv::DigAll); + buf +} + +impl ToStr for T { + #[inline(always)] + fn to_str(&self) -> ~str { + to_str(*self) + } +} + +impl ToStrRadix for T { + #[inline(always)] + fn to_str_radix(&self, radix: uint) -> ~str { + to_str_radix(*self, radix) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::inst::T; + use prelude::*; + + #[test] + fn test_num() { + num::test_num(10 as T, 2 as T); + } + + #[test] + fn test_orderable() { + assert_eq!((1 as T).min(&(2 as T)), 1 as T); + assert_eq!((2 as T).min(&(1 as T)), 1 as T); + assert_eq!((1 as T).max(&(2 as T)), 2 as T); + assert_eq!((2 as T).max(&(1 as T)), 2 as T); + assert_eq!((1 as T).clamp(&(2 as T), &(4 as T)), 2 as T); + assert_eq!((8 as T).clamp(&(2 as T), &(4 as T)), 4 as T); + assert_eq!((3 as T).clamp(&(2 as T), &(4 as T)), 3 as T); + } + + #[test] + fn test_gcd() { + assert_eq!((10 as T).gcd(&2), 2 as T); + assert_eq!((10 as T).gcd(&3), 1 as T); + assert_eq!((0 as T).gcd(&3), 3 as T); + assert_eq!((3 as T).gcd(&3), 3 as T); + assert_eq!((56 as T).gcd(&42), 14 as T); + } + + #[test] + fn test_lcm() { + assert_eq!((1 as T).lcm(&0), 0 as T); + assert_eq!((0 as T).lcm(&1), 0 as T); + assert_eq!((1 as T).lcm(&1), 1 as T); + assert_eq!((8 as T).lcm(&9), 72 as T); + assert_eq!((11 as T).lcm(&5), 55 as T); + assert_eq!((99 as T).lcm(&17), 1683 as T); + } + + #[test] + fn test_multiple_of() { + assert!((6 as T).is_multiple_of(&(6 as T))); + assert!((6 as T).is_multiple_of(&(3 as T))); + assert!((6 as T).is_multiple_of(&(1 as T))); + } + + #[test] + fn test_even() { + assert_eq!((0 as T).is_even(), true); + assert_eq!((1 as T).is_even(), false); + assert_eq!((2 as T).is_even(), true); + assert_eq!((3 as T).is_even(), false); + assert_eq!((4 as T).is_even(), true); + } + + #[test] + fn test_odd() { + assert_eq!((0 as T).is_odd(), false); + assert_eq!((1 as T).is_odd(), true); + assert_eq!((2 as T).is_odd(), false); + assert_eq!((3 as T).is_odd(), true); + assert_eq!((4 as T).is_odd(), false); + } + + #[test] + fn test_bitwise() { + assert_eq!(0b1110 as T, (0b1100 as T).bitor(&(0b1010 as T))); + assert_eq!(0b1000 as T, (0b1100 as T).bitand(&(0b1010 as T))); + assert_eq!(0b0110 as T, (0b1100 as T).bitxor(&(0b1010 as T))); + assert_eq!(0b1110 as T, (0b0111 as T).shl(&(1 as T))); + assert_eq!(0b0111 as T, (0b1110 as T).shr(&(1 as T))); + assert_eq!(max_value - (0b1011 as T), (0b1011 as T).not()); + } + + #[test] + fn test_bitcount() { + assert_eq!((0b010101 as T).population_count(), 3); + } + + #[test] + fn test_primitive() { + assert_eq!(Primitive::bits::<T>(), sys::size_of::<T>() * 8); + assert_eq!(Primitive::bytes::<T>(), sys::size_of::<T>()); + } + + #[test] + pub fn test_to_str() { + assert_eq!(to_str_radix(0 as T, 10u), ~"0"); + assert_eq!(to_str_radix(1 as T, 10u), ~"1"); + assert_eq!(to_str_radix(2 as T, 10u), ~"2"); + assert_eq!(to_str_radix(11 as T, 10u), ~"11"); + assert_eq!(to_str_radix(11 as T, 16u), ~"b"); + assert_eq!(to_str_radix(255 as T, 16u), ~"ff"); + assert_eq!(to_str_radix(0xff as T, 10u), ~"255"); + } + + #[test] + pub fn test_from_str() { + assert_eq!(from_str(~"0"), Some(0u as T)); + assert_eq!(from_str(~"3"), Some(3u as T)); + assert_eq!(from_str(~"10"), Some(10u as T)); + assert_eq!(u32::from_str(~"123456789"), Some(123456789 as u32)); + assert_eq!(from_str(~"00100"), Some(100u as T)); + + assert!(from_str(~"").is_none()); + assert!(from_str(~" ").is_none()); + assert!(from_str(~"x").is_none()); + } + + #[test] + pub fn test_parse_bytes() { + use str::to_bytes; + assert_eq!(parse_bytes(to_bytes(~"123"), 10u), Some(123u as T)); + assert_eq!(parse_bytes(to_bytes(~"1001"), 2u), Some(9u as T)); + assert_eq!(parse_bytes(to_bytes(~"123"), 8u), Some(83u as T)); + assert_eq!(u16::parse_bytes(to_bytes(~"123"), 16u), Some(291u as u16)); + assert_eq!(u16::parse_bytes(to_bytes(~"ffff"), 16u), Some(65535u as u16)); + assert_eq!(parse_bytes(to_bytes(~"z"), 36u), Some(35u as T)); + + assert!(parse_bytes(to_bytes(~"Z"), 10u).is_none()); + assert!(parse_bytes(to_bytes(~"_"), 2u).is_none()); + } + + #[test] + fn test_uint_to_str_overflow() { + let mut u8_val: u8 = 255_u8; + assert_eq!(u8::to_str(u8_val), ~"255"); + + u8_val += 1 as u8; + assert_eq!(u8::to_str(u8_val), ~"0"); + + let mut u16_val: u16 = 65_535_u16; + assert_eq!(u16::to_str(u16_val), ~"65535"); + + u16_val += 1 as u16; + assert_eq!(u16::to_str(u16_val), ~"0"); + + let mut u32_val: u32 = 4_294_967_295_u32; + assert_eq!(u32::to_str(u32_val), ~"4294967295"); + + u32_val += 1 as u32; + assert_eq!(u32::to_str(u32_val), ~"0"); + + let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; + assert_eq!(u64::to_str(u64_val), ~"18446744073709551615"); + + u64_val += 1 as u64; + assert_eq!(u64::to_str(u64_val), ~"0"); + } + + #[test] + fn test_uint_from_str_overflow() { + let mut u8_val: u8 = 255_u8; + assert_eq!(u8::from_str(~"255"), Some(u8_val)); + assert!(u8::from_str(~"256").is_none()); + + u8_val += 1 as u8; + assert_eq!(u8::from_str(~"0"), Some(u8_val)); + assert!(u8::from_str(~"-1").is_none()); + + let mut u16_val: u16 = 65_535_u16; + assert_eq!(u16::from_str(~"65535"), Some(u16_val)); + assert!(u16::from_str(~"65536").is_none()); + + u16_val += 1 as u16; + assert_eq!(u16::from_str(~"0"), Some(u16_val)); + assert!(u16::from_str(~"-1").is_none()); + + let mut u32_val: u32 = 4_294_967_295_u32; + assert_eq!(u32::from_str(~"4294967295"), Some(u32_val)); + assert!(u32::from_str(~"4294967296").is_none()); + + u32_val += 1 as u32; + assert_eq!(u32::from_str(~"0"), Some(u32_val)); + assert!(u32::from_str(~"-1").is_none()); + + let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; + assert_eq!(u64::from_str(~"18446744073709551615"), Some(u64_val)); + assert!(u64::from_str(~"18446744073709551616").is_none()); + + u64_val += 1 as u64; + assert_eq!(u64::from_str(~"0"), Some(u64_val)); + assert!(u64::from_str(~"-1").is_none()); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + pub fn to_str_radix1() { + uint::to_str_radix(100u, 1u); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + pub fn to_str_radix37() { + uint::to_str_radix(100u, 37u); + } + + #[test] + pub fn test_ranges() { + let mut l = ~[]; + + for range(0,3) |i| { + l.push(i); + } + for range_rev(13,10) |i| { + l.push(i); + } + for range_step(20,26,2) |i| { + l.push(i); + } + for range_step(36,30,-2) |i| { + l.push(i); + } + for range_step(max_value - 2, max_value, 2) |i| { + l.push(i); + } + for range_step(max_value - 3, max_value, 2) |i| { + l.push(i); + } + for range_step(min_value + 2, min_value, -2) |i| { + l.push(i); + } + for range_step(min_value + 3, min_value, -2) |i| { + l.push(i); + } + + assert_eq!(l, ~[0,1,2, + 13,12,11, + 20,22,24, + 36,34,32, + max_value-2, + max_value-3,max_value-1, + min_value+2, + min_value+3,min_value+1]); + + // None of the `fail`s should execute. + for range(0,0) |_i| { + fail!("unreachable"); + } + for range_rev(0,0) |_i| { + fail!("unreachable"); + } + for range_step(10,0,1) |_i| { + fail!("unreachable"); + } + for range_step(0,1,-10) |_i| { + fail!("unreachable"); + } + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_range_step_zero_step_up() { + for range_step(0,10,0) |_i| {} + } + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_range_step_zero_step_down() { + for range_step(0,-10,0) |_i| {} + } +} diff --git a/src/libstd/num/uint-template/u16.rs b/src/libstd/num/uint-template/u16.rs new file mode 100644 index 00000000000..cc262f6b4de --- /dev/null +++ b/src/libstd/num/uint-template/u16.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `u16` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = u16; + #[allow(non_camel_case_types)] + pub type T_SIGNED = i16; + pub static bits: uint = 16; + + impl Primitive for u16 { + #[inline(always)] + fn bits() -> uint { 16 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<u16>() / 8 } + } + + impl BitCount for u16 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> u16 { unsafe { intrinsics::ctpop16(*self as i16) as u16 } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> u16 { unsafe { intrinsics::ctlz16(*self as i16) as u16 } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> u16 { unsafe { intrinsics::cttz16(*self as i16) as u16 } } + } +} diff --git a/src/libstd/num/uint-template/u32.rs b/src/libstd/num/uint-template/u32.rs new file mode 100644 index 00000000000..7d7c8e3be30 --- /dev/null +++ b/src/libstd/num/uint-template/u32.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `u32` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = u32; + #[allow(non_camel_case_types)] + pub type T_SIGNED = i32; + pub static bits: uint = 32; + + impl Primitive for u32 { + #[inline(always)] + fn bits() -> uint { 32 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<u32>() / 8 } + } + + impl BitCount for u32 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> u32 { unsafe { intrinsics::ctpop32(*self as i32) as u32 } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlp` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> u32 { unsafe { intrinsics::ctlz32(*self as i32) as u32 } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttp` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> u32 { unsafe { intrinsics::cttz32(*self as i32) as u32 } } + } +} diff --git a/src/libstd/num/uint-template/u64.rs b/src/libstd/num/uint-template/u64.rs new file mode 100644 index 00000000000..756c29950c3 --- /dev/null +++ b/src/libstd/num/uint-template/u64.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `u64` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = u64; + #[allow(non_camel_case_types)] + pub type T_SIGNED = i64; + pub static bits: uint = 64; + + impl Primitive for u64 { + #[inline(always)] + fn bits() -> uint { 64 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<u64>() / 8 } + } + + impl BitCount for u64 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> u64 { unsafe { intrinsics::ctpop64(*self as i64) as u64 } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> u64 { unsafe { intrinsics::ctlz64(*self as i64) as u64 } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> u64 { unsafe { intrinsics::cttz64(*self as i64) as u64 } } + } +} diff --git a/src/libstd/num/uint-template/u8.rs b/src/libstd/num/uint-template/u8.rs new file mode 100644 index 00000000000..5ac860c0359 --- /dev/null +++ b/src/libstd/num/uint-template/u8.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `u8` + +mod inst { + use num::{Primitive, BitCount}; + use unstable::intrinsics; + + pub type T = u8; + #[allow(non_camel_case_types)] + pub type T_SIGNED = i8; + pub static bits: uint = 8; + + impl Primitive for u8 { + #[inline(always)] + fn bits() -> uint { 8 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<u8>() / 8 } + } + + impl BitCount for u8 { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> u8 { unsafe { intrinsics::ctpop8(*self as i8) as u8 } } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> u8 { unsafe { intrinsics::ctlz8(*self as i8) as u8 } } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> u8 { unsafe { intrinsics::cttz8(*self as i8) as u8 } } + } +} diff --git a/src/libstd/num/uint-template/uint.rs b/src/libstd/num/uint-template/uint.rs new file mode 100644 index 00000000000..763c305f221 --- /dev/null +++ b/src/libstd/num/uint-template/uint.rs @@ -0,0 +1,256 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations and constants for `uint` + +pub use self::inst::{ + div_ceil, div_round, div_floor, iterate, + next_power_of_two +}; + +pub mod inst { + use iter; + use num::{Primitive, BitCount}; + use sys; + + pub type T = uint; + #[allow(non_camel_case_types)] + pub type T_SIGNED = int; + + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + #[cfg(target_arch = "mips")] + pub static bits: uint = 32; + + #[cfg(target_arch = "x86_64")] + pub static bits: uint = 64; + + impl Primitive for uint { + #[cfg(target_word_size = "32")] + #[inline(always)] + fn bits() -> uint { 32 } + + #[cfg(target_word_size = "64")] + #[inline(always)] + fn bits() -> uint { 64 } + + #[inline(always)] + fn bytes() -> uint { Primitive::bits::<uint>() / 8 } + } + + #[cfg(target_word_size = "32")] + #[inline(always)] + impl BitCount for uint { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> uint { (*self as i32).population_count() as uint } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> uint { (*self as i32).leading_zeros() as uint } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> uint { (*self as i32).trailing_zeros() as uint } + } + + #[cfg(target_word_size = "64")] + #[inline(always)] + impl BitCount for uint { + /// Counts the number of bits set. Wraps LLVM's `ctpop` intrinsic. + #[inline(always)] + fn population_count(&self) -> uint { (*self as i64).population_count() as uint } + + /// Counts the number of leading zeros. Wraps LLVM's `ctlz` intrinsic. + #[inline(always)] + fn leading_zeros(&self) -> uint { (*self as i64).leading_zeros() as uint } + + /// Counts the number of trailing zeros. Wraps LLVM's `cttz` intrinsic. + #[inline(always)] + fn trailing_zeros(&self) -> uint { (*self as i64).trailing_zeros() as uint } + } + + /// + /// Divide two numbers, return the result, rounded up. + /// + /// # Arguments + /// + /// * x - an integer + /// * y - an integer distinct from 0u + /// + /// # Return value + /// + /// The smallest integer `q` such that `x/y <= q`. + /// + pub fn div_ceil(x: uint, y: uint) -> uint { + let div = x / y; + if x % y == 0u { div } + else { div + 1u } + } + + /// + /// Divide two numbers, return the result, rounded to the closest integer. + /// + /// # Arguments + /// + /// * x - an integer + /// * y - an integer distinct from 0u + /// + /// # Return value + /// + /// The integer `q` closest to `x/y`. + /// + pub fn div_round(x: uint, y: uint) -> uint { + let div = x / y; + if x % y * 2u < y { div } + else { div + 1u } + } + + /// + /// Divide two numbers, return the result, rounded down. + /// + /// Note: This is the same function as `div`. + /// + /// # Arguments + /// + /// * x - an integer + /// * y - an integer distinct from 0u + /// + /// # Return value + /// + /// The smallest integer `q` such that `x/y <= q`. This + /// is either `x/y` or `x/y + 1`. + /// + pub fn div_floor(x: uint, y: uint) -> uint { return x / y; } + + /// + /// Iterate over the range [`lo`..`hi`), or stop when requested + /// + /// # Arguments + /// + /// * lo - The integer at which to start the loop (included) + /// * hi - The integer at which to stop the loop (excluded) + /// * it - A block to execute with each consecutive integer of the range. + /// Return `true` to continue, `false` to stop. + /// + /// # Return value + /// + /// `true` If execution proceeded correctly, `false` if it was interrupted, + /// that is if `it` returned `false` at any point. + /// + pub fn iterate(lo: uint, hi: uint, it: &fn(uint) -> bool) -> bool { + let mut i = lo; + while i < hi { + if (!it(i)) { return false; } + i += 1u; + } + return true; + } + + impl iter::Times for uint { + #[inline(always)] + /// + /// A convenience form for basic iteration. Given a uint `x`, + /// `for x.times { ... }` executes the given block x times. + /// + /// Equivalent to `for uint::range(0, x) |_| { ... }`. + /// + /// Not defined on all integer types to permit unambiguous + /// use with integer literals of inferred integer-type as + /// the self-value (eg. `for 100.times { ... }`). + /// + fn times(&self, it: &fn() -> bool) -> bool { + let mut i = *self; + while i > 0 { + if !it() { return false; } + i -= 1; + } + return true; + } + } + + /// Returns the smallest power of 2 greater than or equal to `n` + #[inline(always)] + pub fn next_power_of_two(n: uint) -> uint { + let halfbits: uint = sys::size_of::<uint>() * 4u; + let mut tmp: uint = n - 1u; + let mut shift: uint = 1u; + while shift <= halfbits { tmp |= tmp >> shift; shift <<= 1u; } + return tmp + 1u; + } + + #[test] + fn test_next_power_of_two() { + assert_eq!(next_power_of_two(0u), 0u); + assert_eq!(next_power_of_two(1u), 1u); + assert_eq!(next_power_of_two(2u), 2u); + assert_eq!(next_power_of_two(3u), 4u); + assert_eq!(next_power_of_two(4u), 4u); + assert_eq!(next_power_of_two(5u), 8u); + assert_eq!(next_power_of_two(6u), 8u); + assert_eq!(next_power_of_two(7u), 8u); + assert_eq!(next_power_of_two(8u), 8u); + assert_eq!(next_power_of_two(9u), 16u); + assert_eq!(next_power_of_two(10u), 16u); + assert_eq!(next_power_of_two(11u), 16u); + assert_eq!(next_power_of_two(12u), 16u); + assert_eq!(next_power_of_two(13u), 16u); + assert_eq!(next_power_of_two(14u), 16u); + assert_eq!(next_power_of_two(15u), 16u); + assert_eq!(next_power_of_two(16u), 16u); + assert_eq!(next_power_of_two(17u), 32u); + assert_eq!(next_power_of_two(18u), 32u); + assert_eq!(next_power_of_two(19u), 32u); + assert_eq!(next_power_of_two(20u), 32u); + assert_eq!(next_power_of_two(21u), 32u); + assert_eq!(next_power_of_two(22u), 32u); + assert_eq!(next_power_of_two(23u), 32u); + assert_eq!(next_power_of_two(24u), 32u); + assert_eq!(next_power_of_two(25u), 32u); + assert_eq!(next_power_of_two(26u), 32u); + assert_eq!(next_power_of_two(27u), 32u); + assert_eq!(next_power_of_two(28u), 32u); + assert_eq!(next_power_of_two(29u), 32u); + assert_eq!(next_power_of_two(30u), 32u); + assert_eq!(next_power_of_two(31u), 32u); + assert_eq!(next_power_of_two(32u), 32u); + assert_eq!(next_power_of_two(33u), 64u); + assert_eq!(next_power_of_two(34u), 64u); + assert_eq!(next_power_of_two(35u), 64u); + assert_eq!(next_power_of_two(36u), 64u); + assert_eq!(next_power_of_two(37u), 64u); + assert_eq!(next_power_of_two(38u), 64u); + assert_eq!(next_power_of_two(39u), 64u); + } + + #[test] + fn test_overflows() { + use uint; + assert!((uint::max_value > 0u)); + assert!((uint::min_value <= 0u)); + assert_eq!(uint::min_value + uint::max_value + 1u, 0u); + } + + #[test] + fn test_div() { + assert_eq!(div_floor(3u, 4u), 0u); + assert_eq!(div_ceil(3u, 4u), 1u); + assert_eq!(div_round(3u, 4u), 1u); + } + + #[test] + pub fn test_times() { + use iter::Times; + let ten = 10 as uint; + let mut accum = 0; + for ten.times { accum += 1; } + assert_eq!(accum, 10); + } +} diff --git a/src/libstd/old_iter.rs b/src/libstd/old_iter.rs new file mode 100644 index 00000000000..389b643572c --- /dev/null +++ b/src/libstd/old_iter.rs @@ -0,0 +1,348 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +**Deprecated** iteration traits and common implementations. + +*/ + +use cmp::{Eq, Ord}; +use kinds::Copy; +use option::{None, Option, Some}; +use vec; + +/// A function used to initialize the elements of a sequence +pub type InitOp<'self,T> = &'self fn(uint) -> T; + +pub trait BaseIter<A> { + fn each(&self, blk: &fn(v: &A) -> bool) -> bool; + fn size_hint(&self) -> Option<uint>; +} + +pub trait ReverseIter<A>: BaseIter<A> { + fn each_reverse(&self, blk: &fn(&A) -> bool) -> bool; +} + +pub trait MutableIter<A>: BaseIter<A> { + fn each_mut(&mut self, blk: &fn(&mut A) -> bool) -> bool; +} + +pub trait ExtendedIter<A> { + fn eachi(&self, blk: &fn(uint, v: &A) -> bool) -> bool; + fn all(&self, blk: &fn(&A) -> bool) -> bool; + fn any(&self, blk: &fn(&A) -> bool) -> bool; + fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B; + fn position(&self, f: &fn(&A) -> bool) -> Option<uint>; + fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B]; + fn flat_map_to_vec<B,IB: BaseIter<B>>(&self, op: &fn(&A) -> IB) -> ~[B]; +} + +pub trait ExtendedMutableIter<A> { + fn eachi_mut(&mut self, blk: &fn(uint, &mut A) -> bool) -> bool; +} + +pub trait EqIter<A:Eq> { + fn contains(&self, x: &A) -> bool; + fn count(&self, x: &A) -> uint; +} + +pub trait CopyableIter<A:Copy> { + fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A]; + fn to_vec(&self) -> ~[A]; + fn find(&self, p: &fn(&A) -> bool) -> Option<A>; +} + +pub trait CopyableOrderedIter<A:Copy + Ord> { + fn min(&self) -> A; + fn max(&self) -> A; +} + +pub trait CopyableNonstrictIter<A:Copy> { + // Like "each", but copies out the value. If the receiver is mutated while + // iterating over it, the semantics must not be memory-unsafe but are + // otherwise undefined. + fn each_val(&const self, f: &fn(A) -> bool) -> bool; +} + +// A trait for sequences that can be built by imperatively pushing elements +// onto them. +pub trait Buildable<A> { + /** + * Builds a buildable sequence by calling a provided function with + * an argument function that pushes an element onto the back of + * the sequence. + * This version takes an initial size for the sequence. + * + * # Arguments + * + * * size - A hint for an initial size of the sequence + * * builder - A function that will construct the sequence. It receives + * as an argument a function that will push an element + * onto the sequence being constructed. + */ + fn build_sized(size: uint, builder: &fn(push: &fn(A))) -> Self; +} + +#[inline(always)] +pub fn _eachi<A,IA:BaseIter<A>>(this: &IA, blk: &fn(uint, &A) -> bool) -> bool { + let mut i = 0; + for this.each |a| { + if !blk(i, a) { + return false; + } + i += 1; + } + return true; +} + +pub fn eachi<A,IA:BaseIter<A>>(this: &IA, blk: &fn(uint, &A) -> bool) -> bool { + _eachi(this, blk) +} + +#[inline(always)] +pub fn all<A,IA:BaseIter<A>>(this: &IA, blk: &fn(&A) -> bool) -> bool { + for this.each |a| { + if !blk(a) { + return false; + } + } + return true; +} + +#[inline(always)] +pub fn any<A,IA:BaseIter<A>>(this: &IA, blk: &fn(&A) -> bool) -> bool { + for this.each |a| { + if blk(a) { + return true; + } + } + return false; +} + +#[inline(always)] +pub fn filter_to_vec<A:Copy,IA:BaseIter<A>>(this: &IA, + prd: &fn(&A) -> bool) + -> ~[A] { + do vec::build_sized_opt(this.size_hint()) |push| { + for this.each |a| { + if prd(a) { push(*a); } + } + } +} + +#[inline(always)] +pub fn map_to_vec<A,B,IA:BaseIter<A>>(this: &IA, op: &fn(&A) -> B) -> ~[B] { + do vec::build_sized_opt(this.size_hint()) |push| { + for this.each |a| { + push(op(a)); + } + } +} + +#[inline(always)] +pub fn flat_map_to_vec<A,B,IA:BaseIter<A>,IB:BaseIter<B>>(this: &IA, + op: &fn(&A) -> IB) + -> ~[B] { + do vec::build |push| { + for this.each |a| { + for op(a).each |&b| { + push(b); + } + } + } +} + +#[inline(always)] +pub fn foldl<A,B,IA:BaseIter<A>>(this: &IA, b0: B, blk: &fn(&B, &A) -> B) + -> B { + let mut b = b0; + for this.each |a| { + b = blk(&b, a); + } + b +} + +#[inline(always)] +pub fn to_vec<A:Copy,IA:BaseIter<A>>(this: &IA) -> ~[A] { + map_to_vec(this, |&x| x) +} + +#[inline(always)] +pub fn contains<A:Eq,IA:BaseIter<A>>(this: &IA, x: &A) -> bool { + for this.each |a| { + if *a == *x { return true; } + } + return false; +} + +#[inline(always)] +pub fn count<A:Eq,IA:BaseIter<A>>(this: &IA, x: &A) -> uint { + do foldl(this, 0) |count, value| { + if *value == *x { + *count + 1 + } else { + *count + } + } +} + +#[inline(always)] +pub fn position<A,IA:BaseIter<A>>(this: &IA, f: &fn(&A) -> bool) + -> Option<uint> { + let mut i = 0; + for this.each |a| { + if f(a) { return Some(i); } + i += 1; + } + return None; +} + +// note: 'rposition' would only make sense to provide with a bidirectional +// iter interface, such as would provide "reach" in addition to "each". As is, +// it would have to be implemented with foldr, which is too inefficient. + +#[inline(always)] +pub fn min<A:Copy + Ord,IA:BaseIter<A>>(this: &IA) -> A { + match do foldl::<A,Option<A>,IA>(this, None) |a, b| { + match a { + &Some(ref a_) if *a_ < *b => { + *(a) + } + _ => Some(*b) + } + } { + Some(val) => val, + None => fail!("min called on empty iterator") + } +} + +#[inline(always)] +pub fn max<A:Copy + Ord,IA:BaseIter<A>>(this: &IA) -> A { + match do foldl::<A,Option<A>,IA>(this, None) |a, b| { + match a { + &Some(ref a_) if *a_ > *b => { + *(a) + } + _ => Some(*b) + } + } { + Some(val) => val, + None => fail!("max called on empty iterator") + } +} + +#[inline(always)] +pub fn find<A:Copy,IA:BaseIter<A>>(this: &IA, f: &fn(&A) -> bool) + -> Option<A> { + for this.each |i| { + if f(i) { return Some(*i) } + } + return None; +} + +// Some functions for just building + +/** + * Builds a sequence by calling a provided function with an argument + * function that pushes an element to the back of a sequence. + * + * # Arguments + * + * * builder - A function that will construct the sequence. It receives + * as an argument a function that will push an element + * onto the sequence being constructed. + */ +#[inline(always)] +pub fn build<A,B: Buildable<A>>(builder: &fn(push: &fn(A))) -> B { + Buildable::build_sized(4, builder) +} + +/** + * Builds a sequence by calling a provided function with an argument + * function that pushes an element to the back of the sequence. + * This version takes an initial size for the sequence. + * + * # Arguments + * + * * size - An option, maybe containing initial size of the sequence + * to reserve. + * * builder - A function that will construct the sequence. It receives + * as an argument a function that will push an element + * onto the sequence being constructed. + */ +#[inline(always)] +pub fn build_sized_opt<A,B: Buildable<A>>(size: Option<uint>, + builder: &fn(push: &fn(A))) -> B { + Buildable::build_sized(size.get_or_default(4), builder) +} + +// Functions that combine iteration and building + +/// Applies a function to each element of an iterable and returns the results +/// in a sequence built via `BU`. See also `map_to_vec`. +#[inline(always)] +pub fn map<T,IT: BaseIter<T>,U,BU: Buildable<U>>(v: &IT, f: &fn(&T) -> U) + -> BU { + do build_sized_opt(v.size_hint()) |push| { + for v.each() |elem| { + push(f(elem)); + } + } +} + +/** + * Creates and initializes a generic sequence from a function. + * + * Creates a generic sequence of size `n_elts` and initializes the elements + * to the value returned by the function `op`. + */ +#[inline(always)] +pub fn from_fn<T,BT: Buildable<T>>(n_elts: uint, op: InitOp<T>) -> BT { + do Buildable::build_sized(n_elts) |push| { + let mut i: uint = 0u; + while i < n_elts { push(op(i)); i += 1u; } + } +} + +/** + * Creates and initializes a generic sequence with some elements. + * + * Creates an immutable vector of size `n_elts` and initializes the elements + * to the value `t`. + */ +#[inline(always)] +pub fn from_elem<T:Copy,BT:Buildable<T>>(n_elts: uint, t: T) -> BT { + do Buildable::build_sized(n_elts) |push| { + let mut i: uint = 0; + while i < n_elts { push(t); i += 1; } + } +} + +/// Appends two generic sequences. +#[inline(always)] +pub fn append<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(lhs: &IT, rhs: &IT) + -> BT { + let size_opt = lhs.size_hint().chain_ref( + |sz1| rhs.size_hint().map(|sz2| *sz1+*sz2)); + do build_sized_opt(size_opt) |push| { + for lhs.each |x| { push(*x); } + for rhs.each |x| { push(*x); } + } +} + +/// Copies a generic sequence, possibly converting it to a different +/// type of sequence. +#[inline(always)] +pub fn copy_seq<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(v: &IT) -> BT { + do build_sized_opt(v.size_hint()) |push| { + for v.each |x| { push(*x); } + } +} diff --git a/src/libstd/ops.rs b/src/libstd/ops.rs new file mode 100644 index 00000000000..47ff45be687 --- /dev/null +++ b/src/libstd/ops.rs @@ -0,0 +1,81 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Traits for the built-in operators + +#[lang="drop"] +pub trait Drop { + fn finalize(&self); // FIXME(#4332): Rename to "drop"? --pcwalton +} + +#[lang="add"] +pub trait Add<RHS,Result> { + fn add(&self, rhs: &RHS) -> Result; +} + +#[lang="sub"] +pub trait Sub<RHS,Result> { + fn sub(&self, rhs: &RHS) -> Result; +} + +#[lang="mul"] +pub trait Mul<RHS,Result> { + fn mul(&self, rhs: &RHS) -> Result; +} + +#[lang="div"] +pub trait Div<RHS,Result> { + fn div(&self, rhs: &RHS) -> Result; +} + +#[lang="rem"] +pub trait Rem<RHS,Result> { + fn rem(&self, rhs: &RHS) -> Result; +} + +#[lang="neg"] +pub trait Neg<Result> { + fn neg(&self) -> Result; +} + +#[lang="not"] +pub trait Not<Result> { + fn not(&self) -> Result; +} + +#[lang="bitand"] +pub trait BitAnd<RHS,Result> { + fn bitand(&self, rhs: &RHS) -> Result; +} + +#[lang="bitor"] +pub trait BitOr<RHS,Result> { + fn bitor(&self, rhs: &RHS) -> Result; +} + +#[lang="bitxor"] +pub trait BitXor<RHS,Result> { + fn bitxor(&self, rhs: &RHS) -> Result; +} + +#[lang="shl"] +pub trait Shl<RHS,Result> { + fn shl(&self, rhs: &RHS) -> Result; +} + +#[lang="shr"] +pub trait Shr<RHS,Result> { + fn shr(&self, rhs: &RHS) -> Result; +} + +#[lang="index"] +pub trait Index<Index,Result> { + fn index(&self, index: &Index) -> Result; +} diff --git a/src/libstd/option.rs b/src/libstd/option.rs new file mode 100644 index 00000000000..bc1ffcdc81a --- /dev/null +++ b/src/libstd/option.rs @@ -0,0 +1,468 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Operations on the ubiquitous `Option` type. + +Type `Option` represents an optional value. + +Every `Option<T>` value can either be `Some(T)` or `None`. Where in other +languages you might use a nullable type, in Rust you would use an option +type. + +Options are most commonly used with pattern matching to query the presence +of a value and take action, always accounting for the `None` case. + +# Example + +~~~ +let msg = Some(~"howdy"); + +// Take a reference to the contained string +match msg { + Some(ref m) => io::println(m), + None => () +} + +// Remove the contained string, destroying the Option +let unwrapped_msg = match msg { + Some(m) => m, + None => ~"default message" +}; +~~~ + +*/ + +use cmp::{Eq,Ord}; +use ops::Add; +use kinds::Copy; +use util; +use num::Zero; +use old_iter::{BaseIter, MutableIter, ExtendedIter}; +use old_iter; +use str::StrSlice; +use clone::DeepClone; + +#[cfg(test)] use str; + +/// The option type +#[deriving(Clone, Eq)] +pub enum Option<T> { + None, + Some(T), +} + +impl<T: DeepClone> DeepClone for Option<T> { + fn deep_clone(&self) -> Option<T> { + match *self { + Some(ref x) => Some(x.deep_clone()), + None => None + } + } +} + +impl<T:Ord> Ord for Option<T> { + fn lt(&self, other: &Option<T>) -> bool { + match (self, other) { + (&None, &None) => false, + (&None, &Some(_)) => true, + (&Some(_), &None) => false, + (&Some(ref a), &Some(ref b)) => *a < *b + } + } + + fn le(&self, other: &Option<T>) -> bool { + match (self, other) { + (&None, &None) => true, + (&None, &Some(_)) => true, + (&Some(_), &None) => false, + (&Some(ref a), &Some(ref b)) => *a <= *b + } + } + + fn ge(&self, other: &Option<T>) -> bool { + !(self < other) + } + + fn gt(&self, other: &Option<T>) -> bool { + !(self <= other) + } +} + +impl<T: Copy + Add<T,T>> Add<Option<T>, Option<T>> for Option<T> { + #[inline(always)] + fn add(&self, other: &Option<T>) -> Option<T> { + match (*self, *other) { + (None, None) => None, + (_, None) => *self, + (None, _) => *other, + (Some(ref lhs), Some(ref rhs)) => Some(*lhs + *rhs) + } + } +} + +impl<T> BaseIter<T> for Option<T> { + /// Performs an operation on the contained value by reference + #[inline(always)] + fn each<'a>(&'a self, f: &fn(x: &'a T) -> bool) -> bool { + match *self { None => true, Some(ref t) => { f(t) } } + } + + #[inline(always)] + fn size_hint(&self) -> Option<uint> { + if self.is_some() { Some(1) } else { Some(0) } + } +} + +impl<T> MutableIter<T> for Option<T> { + #[inline(always)] + fn each_mut<'a>(&'a mut self, f: &fn(&'a mut T) -> bool) -> bool { + match *self { None => true, Some(ref mut t) => { f(t) } } + } +} + +impl<A> ExtendedIter<A> for Option<A> { + pub fn eachi(&self, blk: &fn(uint, v: &A) -> bool) -> bool { + old_iter::eachi(self, blk) + } + pub fn all(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::all(self, blk) + } + pub fn any(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::any(self, blk) + } + pub fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B { + old_iter::foldl(self, b0, blk) + } + pub fn position(&self, f: &fn(&A) -> bool) -> Option<uint> { + old_iter::position(self, f) + } + fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B] { + old_iter::map_to_vec(self, op) + } + fn flat_map_to_vec<B,IB:BaseIter<B>>(&self, op: &fn(&A) -> IB) + -> ~[B] { + old_iter::flat_map_to_vec(self, op) + } +} + +pub impl<T> Option<T> { + /// Returns true if the option equals `none` + fn is_none(&const self) -> bool { + match *self { None => true, Some(_) => false } + } + + /// Returns true if the option contains some value + #[inline(always)] + fn is_some(&const self) -> bool { !self.is_none() } + + /// Update an optional value by optionally running its content through a + /// function that returns an option. + #[inline(always)] + fn chain<U>(self, f: &fn(t: T) -> Option<U>) -> Option<U> { + + match self { + Some(t) => f(t), + None => None + } + } + + /// Returns the leftmost Some() value, or None if both are None. + #[inline(always)] + fn or(self, optb: Option<T>) -> Option<T> { + match self { + Some(opta) => Some(opta), + _ => optb + } + } + + /// Update an optional value by optionally running its content by reference + /// through a function that returns an option. + #[inline(always)] + fn chain_ref<'a, U>(&'a self, f: &fn(x: &'a T) -> Option<U>) -> Option<U> { + match *self { Some(ref x) => f(x), None => None } + } + + /// Maps a `some` value from one type to another by reference + #[inline(always)] + fn map<'a, U>(&self, f: &fn(&'a T) -> U) -> Option<U> { + match *self { Some(ref x) => Some(f(x)), None => None } + } + + /// As `map`, but consumes the option and gives `f` ownership to avoid + /// copying. + #[inline(always)] + fn map_consume<U>(self, f: &fn(v: T) -> U) -> Option<U> { + match self { None => None, Some(v) => Some(f(v)) } + } + + /// Applies a function to the contained value or returns a default + #[inline(always)] + fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U { + match *self { None => def, Some(ref t) => f(t) } + } + + /// As `map_default`, but consumes the option and gives `f` + /// ownership to avoid copying. + #[inline(always)] + fn map_consume_default<U>(self, def: U, f: &fn(v: T) -> U) -> U { + match self { None => def, Some(v) => f(v) } + } + + /// Apply a function to the contained value or do nothing + fn mutate(&mut self, f: &fn(T) -> T) { + if self.is_some() { + *self = Some(f(self.swap_unwrap())); + } + } + + /// Apply a function to the contained value or set it to a default + fn mutate_default(&mut self, def: T, f: &fn(T) -> T) { + if self.is_some() { + *self = Some(f(self.swap_unwrap())); + } else { + *self = Some(def); + } + } + + /** + Gets an immutable reference to the value inside an option. + + # Failure + + Fails if the value equals `None` + + # Safety note + + In general, because this function may fail, its use is discouraged + (calling `get` on `None` is akin to dereferencing a null pointer). + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + #[inline(always)] + fn get_ref<'a>(&'a self) -> &'a T { + match *self { + Some(ref x) => x, + None => fail!("option::get_ref none") + } + } + + /** + Gets a mutable reference to the value inside an option. + + # Failure + + Fails if the value equals `None` + + # Safety note + + In general, because this function may fail, its use is discouraged + (calling `get` on `None` is akin to dereferencing a null pointer). + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + #[inline(always)] + fn get_mut_ref<'a>(&'a mut self) -> &'a mut T { + match *self { + Some(ref mut x) => x, + None => fail!("option::get_mut_ref none") + } + } + + #[inline(always)] + fn unwrap(self) -> T { + /*! + Moves a value out of an option type and returns it. + + Useful primarily for getting strings, vectors and unique pointers out + of option types without copying them. + + # Failure + + Fails if the value equals `None`. + + # Safety note + + In general, because this function may fail, its use is discouraged. + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + match self { + Some(x) => x, + None => fail!("option::unwrap none") + } + } + + /** + * The option dance. Moves a value out of an option type and returns it, + * replacing the original with `None`. + * + * # Failure + * + * Fails if the value equals `None`. + */ + #[inline(always)] + fn swap_unwrap(&mut self) -> T { + if self.is_none() { fail!("option::swap_unwrap none") } + util::replace(self, None).unwrap() + } + + /** + * Gets the value out of an option, printing a specified message on + * failure + * + * # Failure + * + * Fails if the value equals `none` + */ + #[inline(always)] + fn expect(self, reason: &str) -> T { + match self { + Some(val) => val, + None => fail!(reason.to_owned()), + } + } +} + +pub impl<T:Copy> Option<T> { + /** + Gets the value out of an option + + # Failure + + Fails if the value equals `None` + + # Safety note + + In general, because this function may fail, its use is discouraged + (calling `get` on `None` is akin to dereferencing a null pointer). + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + #[inline(always)] + fn get(self) -> T { + match self { + Some(copy x) => return x, + None => fail!("option::get none") + } + } + + /// Returns the contained value or a default + #[inline(always)] + fn get_or_default(self, def: T) -> T { + match self { Some(copy x) => x, None => def } + } + + /// Applies a function zero or more times until the result is none. + #[inline(always)] + fn while_some(self, blk: &fn(v: T) -> Option<T>) { + let mut opt = self; + while opt.is_some() { + opt = blk(opt.unwrap()); + } + } +} + +pub impl<T:Copy + Zero> Option<T> { + /// Returns the contained value or zero (for this type) + #[inline(always)] + fn get_or_zero(self) -> T { + match self { Some(copy x) => x, None => Zero::zero() } + } +} + +#[test] +fn test_unwrap_ptr() { + unsafe { + let x = ~0; + let addr_x: *int = ::cast::transmute(&*x); + let opt = Some(x); + let y = opt.unwrap(); + let addr_y: *int = ::cast::transmute(&*y); + assert_eq!(addr_x, addr_y); + } +} + +#[test] +fn test_unwrap_str() { + let x = ~"test"; + let addr_x = str::as_buf(x, |buf, _len| buf); + let opt = Some(x); + let y = opt.unwrap(); + let addr_y = str::as_buf(y, |buf, _len| buf); + assert_eq!(addr_x, addr_y); +} + +#[test] +fn test_unwrap_resource() { + struct R { + i: @mut int, + } + + #[unsafe_destructor] + impl ::ops::Drop for R { + fn finalize(&self) { *(self.i) += 1; } + } + + fn R(i: @mut int) -> R { + R { + i: i + } + } + + let i = @mut 0; + { + let x = R(i); + let opt = Some(x); + let _y = opt.unwrap(); + } + assert_eq!(*i, 1); +} + +#[test] +fn test_option_dance() { + let x = Some(()); + let mut y = Some(5); + let mut y2 = 0; + for x.each |_x| { + y2 = y.swap_unwrap(); + } + assert_eq!(y2, 5); + assert!(y.is_none()); +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_option_too_much_dance() { + let mut y = Some(util::NonCopyable()); + let _y2 = y.swap_unwrap(); + let _y3 = y.swap_unwrap(); +} + +#[test] +fn test_option_while_some() { + let mut i = 0; + do Some(10).while_some |j| { + i += 1; + if (j > 0) { + Some(j-1) + } else { + None + } + } + assert_eq!(i, 11); +} + +#[test] +fn test_get_or_zero() { + let some_stuff = Some(42); + assert_eq!(some_stuff.get_or_zero(), 42); + let no_stuff: Option<int> = None; + assert_eq!(no_stuff.get_or_zero(), 0); +} diff --git a/src/libstd/os.rs b/src/libstd/os.rs new file mode 100644 index 00000000000..b2a30e50992 --- /dev/null +++ b/src/libstd/os.rs @@ -0,0 +1,1670 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + * Higher-level interfaces to libc::* functions and operating system services. + * + * In general these take and return rust types, use rust idioms (enums, + * closures, vectors) rather than C idioms, and do more extensive safety + * checks. + * + * This module is not meant to only contain 1:1 mappings to libc entries; any + * os-interface code that is reasonably useful and broadly applicable can go + * here. Including utility routines that merely build on other os code. + * + * We assume the general case is that users do not care, and do not want to + * be made to care, which operating system they are on. While they may want + * to special case various special cases -- and so we will not _hide_ the + * facts of which OS the user is on -- they should be given the opportunity + * to write OS-ignorant code by default. + */ + +use cast; +use io; +use libc; +use libc::{c_char, c_void, c_int, size_t}; +use libc::{mode_t, FILE}; +use option; +use option::{Some, None}; +use prelude::*; +use ptr; +use str; +use uint; +use unstable::finally::Finally; +use vec; + +pub use libc::fclose; +pub use os::consts::*; + +pub fn close(fd: c_int) -> c_int { + unsafe { + libc::close(fd) + } +} + +pub mod rustrt { + use libc::{c_char, c_int}; + use libc; + + pub extern { + unsafe fn rust_get_argc() -> c_int; + unsafe fn rust_get_argv() -> **c_char; + unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int; + unsafe fn rust_path_exists(path: *libc::c_char) -> c_int; + unsafe fn rust_set_exit_status(code: libc::intptr_t); + } +} + +pub static TMPBUF_SZ : uint = 1000u; +static BUF_BYTES : uint = 2048u; + +pub fn getcwd() -> Path { + let buf = [0 as libc::c_char, ..BUF_BYTES]; + unsafe { + if(0 as *libc::c_char == libc::getcwd( + &buf[0], + BUF_BYTES as libc::size_t)) { + fail!(); + } + Path(str::raw::from_c_str(&buf[0])) + } +} + +// FIXME: move these to str perhaps? #2620 + +pub fn as_c_charp<T>(s: &str, f: &fn(*c_char) -> T) -> T { + str::as_c_str(s, |b| f(b as *c_char)) +} + +pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool) + -> Option<~str> { + let mut buf = vec::from_elem(TMPBUF_SZ, 0u8 as c_char); + do vec::as_mut_buf(buf) |b, sz| { + if f(b, sz as size_t) { + unsafe { + Some(str::raw::from_buf(b as *u8)) + } + } else { + None + } + } +} + +#[cfg(windows)] +pub mod win32 { + use libc; + use vec; + use str; + use option::{None, Option}; + use option; + use os::TMPBUF_SZ; + use libc::types::os::arch::extra::DWORD; + + pub fn fill_utf16_buf_and_decode(f: &fn(*mut u16, DWORD) -> DWORD) + -> Option<~str> { + unsafe { + let mut n = TMPBUF_SZ as DWORD; + let mut res = None; + let mut done = false; + while !done { + let mut k: DWORD = 0; + let mut buf = vec::from_elem(n as uint, 0u16); + do vec::as_mut_buf(buf) |b, _sz| { + k = f(b, TMPBUF_SZ as DWORD); + if k == (0 as DWORD) { + done = true; + } else if (k == n && + libc::GetLastError() == + libc::ERROR_INSUFFICIENT_BUFFER as DWORD) { + n *= (2 as DWORD); + } else { + done = true; + } + } + if k != 0 && done { + let sub = vec::slice(buf, 0u, k as uint); + res = option::Some(str::from_utf16(sub)); + } + } + return res; + } + } + + pub fn as_utf16_p<T>(s: &str, f: &fn(*u16) -> T) -> T { + let mut t = str::to_utf16(s); + // Null terminate before passing on. + t += ~[0u16]; + vec::as_imm_buf(t, |buf, _len| f(buf)) + } +} + +/* +Accessing environment variables is not generally threadsafe. +Serialize access through a global lock. +*/ +fn with_env_lock<T>(f: &fn() -> T) -> T { + use unstable::finally::Finally; + + unsafe { + return do (|| { + rust_take_env_lock(); + f() + }).finally { + rust_drop_env_lock(); + }; + } + + extern { + #[fast_ffi] + fn rust_take_env_lock(); + #[fast_ffi] + fn rust_drop_env_lock(); + } +} + +pub fn env() -> ~[(~str,~str)] { + unsafe { + #[cfg(windows)] + unsafe fn get_env_pairs() -> ~[~str] { + use libc::types::os::arch::extra::LPTCH; + use libc::funcs::extra::kernel32::{ + GetEnvironmentStringsA, + FreeEnvironmentStringsA + }; + let ch = GetEnvironmentStringsA(); + if (ch as uint == 0) { + fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); + } + let mut curr_ptr: uint = ch as uint; + let mut result = ~[]; + while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) { + let env_pair = str::raw::from_c_str( + curr_ptr as *libc::c_char); + result.push(env_pair); + curr_ptr += + libc::strlen(curr_ptr as *libc::c_char) as uint + + 1; + } + FreeEnvironmentStringsA(ch); + result + } + #[cfg(unix)] + unsafe fn get_env_pairs() -> ~[~str] { + extern { + unsafe fn rust_env_pairs() -> **libc::c_char; + } + let environ = rust_env_pairs(); + if (environ as uint == 0) { + fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); + } + let mut result = ~[]; + ptr::array_each(environ, |e| { + let env_pair = str::raw::from_c_str(e); + debug!("get_env_pairs: %s", + env_pair); + result.push(env_pair); + }); + result + } + + fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] { + let mut pairs = ~[]; + for input.each |p| { + let mut vs = ~[]; + for str::each_splitn_char(*p, '=', 1) |s| { vs.push(s.to_owned()) } + debug!("splitting: len: %u", + vs.len()); + assert_eq!(vs.len(), 2); + pairs.push((copy vs[0], copy vs[1])); + } + pairs + } + do with_env_lock { + let unparsed_environ = get_env_pairs(); + env_convert(unparsed_environ) + } + } +} + +#[cfg(unix)] +pub fn getenv(n: &str) -> Option<~str> { + unsafe { + do with_env_lock { + let s = str::as_c_str(n, |s| libc::getenv(s)); + if ptr::null::<u8>() == cast::transmute(s) { + option::None::<~str> + } else { + let s = cast::transmute(s); + option::Some::<~str>(str::raw::from_buf(s)) + } + } + } +} + +#[cfg(windows)] +pub fn getenv(n: &str) -> Option<~str> { + unsafe { + do with_env_lock { + use os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; + do as_utf16_p(n) |u| { + do fill_utf16_buf_and_decode() |buf, sz| { + libc::GetEnvironmentVariableW(u, buf, sz) + } + } + } + } +} + + +#[cfg(unix)] +pub fn setenv(n: &str, v: &str) { + unsafe { + do with_env_lock { + do str::as_c_str(n) |nbuf| { + do str::as_c_str(v) |vbuf| { + libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1); + } + } + } + } +} + + +#[cfg(windows)] +pub fn setenv(n: &str, v: &str) { + unsafe { + do with_env_lock { + use os::win32::as_utf16_p; + do as_utf16_p(n) |nbuf| { + do as_utf16_p(v) |vbuf| { + libc::SetEnvironmentVariableW(nbuf, vbuf); + } + } + } + } +} + +/// Remove a variable from the environment entirely +pub fn unsetenv(n: &str) { + #[cfg(unix)] + fn _unsetenv(n: &str) { + unsafe { + do with_env_lock { + do str::as_c_str(n) |nbuf| { + libc::funcs::posix01::unistd::unsetenv(nbuf); + } + } + } + } + #[cfg(windows)] + fn _unsetenv(n: &str) { + unsafe { + do with_env_lock { + use os::win32::as_utf16_p; + do as_utf16_p(n) |nbuf| { + libc::SetEnvironmentVariableW(nbuf, ptr::null()); + } + } + } + } + + _unsetenv(n); +} + +pub fn fdopen(fd: c_int) -> *FILE { + unsafe { + return do as_c_charp("r") |modebuf| { + libc::fdopen(fd, modebuf) + }; + } +} + + +// fsync related + +#[cfg(windows)] +pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int { + unsafe { + use libc::funcs::extra::msvcrt::*; + return commit(fd); + } +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int { + unsafe { + use libc::funcs::posix01::unistd::*; + match level { + io::fsync::FSync + | io::fsync::FullFSync => return fsync(fd), + io::fsync::FDataSync => return fdatasync(fd) + } + } +} + +#[cfg(target_os = "macos")] +pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int { + unsafe { + use libc::consts::os::extra::*; + use libc::funcs::posix88::fcntl::*; + use libc::funcs::posix01::unistd::*; + match level { + io::fsync::FSync => return fsync(fd), + _ => { + // According to man fnctl, the ok retval is only specified to be + // !=-1 + if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int) + { return -1 as c_int; } + else + { return 0 as c_int; } + } + } + } +} + +#[cfg(target_os = "freebsd")] +pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int { + unsafe { + use libc::funcs::posix01::unistd::*; + return fsync(fd); + } +} + +pub struct Pipe { + in: c_int, + out: c_int +} + +#[cfg(unix)] +pub fn pipe() -> Pipe { + unsafe { + let mut fds = Pipe {in: 0 as c_int, + out: 0 as c_int }; + assert_eq!(libc::pipe(&mut fds.in), (0 as c_int)); + return Pipe {in: fds.in, out: fds.out}; + } +} + + + +#[cfg(windows)] +pub fn pipe() -> Pipe { + unsafe { + // Windows pipes work subtly differently than unix pipes, and their + // inheritance has to be handled in a different way that I do not + // fully understand. Here we explicitly make the pipe non-inheritable, + // which means to pass it to a subprocess they need to be duplicated + // first, as in core::run. + let mut fds = Pipe {in: 0 as c_int, + out: 0 as c_int }; + let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint, + (libc::O_BINARY | libc::O_NOINHERIT) as c_int); + assert_eq!(res, 0 as c_int); + assert!((fds.in != -1 as c_int && fds.in != 0 as c_int)); + assert!((fds.out != -1 as c_int && fds.in != 0 as c_int)); + return Pipe {in: fds.in, out: fds.out}; + } +} + +fn dup2(src: c_int, dst: c_int) -> c_int { + unsafe { + libc::dup2(src, dst) + } +} + + +pub fn dll_filename(base: &str) -> ~str { + return str::to_owned(DLL_PREFIX) + str::to_owned(base) + + str::to_owned(DLL_SUFFIX) +} + + +pub fn self_exe_path() -> Option<Path> { + + #[cfg(target_os = "freebsd")] + fn load_self() -> Option<~str> { + unsafe { + use libc::funcs::bsd44::*; + use libc::consts::os::extra::*; + do fill_charp_buf() |buf, sz| { + let mib = ~[CTL_KERN as c_int, + KERN_PROC as c_int, + KERN_PROC_PATHNAME as c_int, -1 as c_int]; + let mut sz = sz; + sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint, + buf as *mut c_void, &mut sz, ptr::null(), + 0u as size_t) == (0 as c_int) + } + } + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn load_self() -> Option<~str> { + unsafe { + use libc::funcs::posix01::unistd::readlink; + + let mut path_str = str::with_capacity(TMPBUF_SZ); + let len = do str::as_c_str(path_str) |buf| { + let buf = buf as *mut c_char; + do as_c_charp("/proc/self/exe") |proc_self_buf| { + readlink(proc_self_buf, buf, TMPBUF_SZ as size_t) + } + }; + if len == -1 { + None + } else { + str::raw::set_len(&mut path_str, len as uint); + Some(path_str) + } + } + } + + #[cfg(target_os = "macos")] + fn load_self() -> Option<~str> { + unsafe { + do fill_charp_buf() |buf, sz| { + let mut sz = sz as u32; + libc::funcs::extra::_NSGetExecutablePath( + buf, &mut sz) == (0 as c_int) + } + } + } + + #[cfg(windows)] + fn load_self() -> Option<~str> { + unsafe { + use os::win32::fill_utf16_buf_and_decode; + do fill_utf16_buf_and_decode() |buf, sz| { + libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) + } + } + } + + do load_self().map |pth| { + Path(*pth).dir_path() + } +} + + +/** + * Returns the path to the user's home directory, if known. + * + * On Unix, returns the value of the 'HOME' environment variable if it is set + * and not equal to the empty string. + * + * On Windows, returns the value of the 'HOME' environment variable if it is + * set and not equal to the empty string. Otherwise, returns the value of the + * 'USERPROFILE' environment variable if it is set and not equal to the empty + * string. + * + * Otherwise, homedir returns option::none. + */ +pub fn homedir() -> Option<Path> { + return match getenv("HOME") { + Some(ref p) => if !str::is_empty(*p) { + Some(Path(*p)) + } else { + secondary() + }, + None => secondary() + }; + + #[cfg(unix)] + fn secondary() -> Option<Path> { + None + } + + #[cfg(windows)] + fn secondary() -> Option<Path> { + do getenv(~"USERPROFILE").chain |p| { + if !str::is_empty(p) { + Some(Path(p)) + } else { + None + } + } + } +} + +/** + * Returns the path to a temporary directory. + * + * On Unix, returns the value of the 'TMPDIR' environment variable if it is + * set and non-empty and '/tmp' otherwise. + * + * On Windows, returns the value of, in order, the 'TMP', 'TEMP', + * 'USERPROFILE' environment variable if any are set and not the empty + * string. Otherwise, tmpdir returns the path to the Windows directory. + */ +pub fn tmpdir() -> Path { + return lookup(); + + fn getenv_nonempty(v: &str) -> Option<Path> { + match getenv(v) { + Some(x) => + if str::is_empty(x) { + None + } else { + Some(Path(x)) + }, + _ => None + } + } + + #[cfg(unix)] + #[allow(non_implicitly_copyable_typarams)] + fn lookup() -> Path { + getenv_nonempty("TMPDIR").get_or_default(Path("/tmp")) + } + + #[cfg(windows)] + #[allow(non_implicitly_copyable_typarams)] + fn lookup() -> Path { + getenv_nonempty("TMP").or( + getenv_nonempty("TEMP").or( + getenv_nonempty("USERPROFILE").or( + getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows")) + } +} + +/// Recursively walk a directory structure +pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { + list_dir(p).each(|q| { + let path = &p.push(*q); + f(path) && (!path_is_dir(path) || walk_dir(path, f)) + }) +} + +/// Indicates whether a path represents a directory +pub fn path_is_dir(p: &Path) -> bool { + unsafe { + do str::as_c_str(p.to_str()) |buf| { + rustrt::rust_path_is_dir(buf) != 0 as c_int + } + } +} + +/// Indicates whether a path exists +pub fn path_exists(p: &Path) -> bool { + unsafe { + do str::as_c_str(p.to_str()) |buf| { + rustrt::rust_path_exists(buf) != 0 as c_int + } + } +} + +/** + * Convert a relative path to an absolute path + * + * If the given path is relative, return it prepended with the current working + * directory. If the given path is already an absolute path, return it + * as is. + */ +// NB: this is here rather than in path because it is a form of environment +// querying; what it does depends on the process working directory, not just +// the input paths. +pub fn make_absolute(p: &Path) -> Path { + if p.is_absolute { + copy *p + } else { + getcwd().push_many(p.components) + } +} + + +/// Creates a directory at the specified path +pub fn make_dir(p: &Path, mode: c_int) -> bool { + return mkdir(p, mode); + + #[cfg(windows)] + fn mkdir(p: &Path, _mode: c_int) -> bool { + unsafe { + use os::win32::as_utf16_p; + // FIXME: turn mode into something useful? #2623 + do as_utf16_p(p.to_str()) |buf| { + libc::CreateDirectoryW(buf, unsafe { + cast::transmute(0) + }) + != (0 as libc::BOOL) + } + } + } + + #[cfg(unix)] + fn mkdir(p: &Path, mode: c_int) -> bool { + unsafe { + do as_c_charp(p.to_str()) |c| { + libc::mkdir(c, mode as mode_t) == (0 as c_int) + } + } + } +} + +/// Creates a directory with a given mode. +/// Returns true iff creation +/// succeeded. Also creates all intermediate subdirectories +/// if they don't already exist, giving all of them the same mode. + +// tjc: if directory exists but with different permissions, +// should we return false? +pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { + if path_is_dir(p) { + return true; + } + else if p.components.is_empty() { + return false; + } + else if p.components.len() == 1 { + // No parent directories to create + path_is_dir(p) || make_dir(p, mode) + } + else { + mkdir_recursive(&p.pop(), mode) && make_dir(p, mode) + } +} + +/// Lists the contents of a directory +#[allow(non_implicitly_copyable_typarams)] +pub fn list_dir(p: &Path) -> ~[~str] { + unsafe { + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + #[cfg(target_os = "freebsd")] + #[cfg(target_os = "macos")] + unsafe fn get_list(p: &Path) -> ~[~str] { + use libc::{dirent_t}; + use libc::{opendir, readdir, closedir}; + extern { + unsafe fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; + } + let input = p.to_str(); + let mut strings = ~[]; + let input_ptr = ::cast::transmute(&input[0]); + debug!("os::list_dir -- BEFORE OPENDIR"); + let dir_ptr = opendir(input_ptr); + if (dir_ptr as uint != 0) { + debug!("os::list_dir -- opendir() SUCCESS"); + let mut entry_ptr = readdir(dir_ptr); + while (entry_ptr as uint != 0) { + strings.push(str::raw::from_c_str(rust_list_dir_val( + entry_ptr))); + entry_ptr = readdir(dir_ptr); + } + closedir(dir_ptr); + } + else { + debug!("os::list_dir -- opendir() FAILURE"); + } + debug!( + "os::list_dir -- AFTER -- #: %?", + strings.len()); + strings + } + #[cfg(windows)] + unsafe fn get_list(p: &Path) -> ~[~str] { + use libc::types::os::arch::extra::{LPCTSTR, HANDLE, BOOL}; + use libc::consts::os::extra::INVALID_HANDLE_VALUE; + use libc::wcslen; + use libc::funcs::extra::kernel32::{ + FindFirstFileW, + FindNextFileW, + FindClose, + }; + use os::win32::{ + as_utf16_p + }; + use rt::global_heap::{malloc_raw, free_raw}; + #[nolink] + extern { + unsafe fn rust_list_dir_wfd_size() -> libc::size_t; + unsafe fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) + -> *u16; + } + fn star(p: &Path) -> Path { p.push("*") } + do as_utf16_p(star(p).to_str()) |path_ptr| { + let mut strings = ~[]; + let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); + let find_handle = + FindFirstFileW( + path_ptr, + ::cast::transmute(wfd_ptr)); + if find_handle as int != INVALID_HANDLE_VALUE { + let mut more_files = 1 as libc::c_int; + while more_files != 0 { + let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); + if fp_buf as uint == 0 { + fail!("os::list_dir() failure: got null ptr from wfd"); + } + else { + let fp_vec = vec::from_buf( + fp_buf, wcslen(fp_buf) as uint); + let fp_str = str::from_utf16(fp_vec); + strings.push(fp_str); + } + more_files = FindNextFileW( + find_handle, + ::cast::transmute(wfd_ptr)); + } + FindClose(find_handle); + free_raw(wfd_ptr); + } + strings + } + } + do get_list(p).filtered |filename| { + *filename != ~"." && *filename != ~".." + } + } +} + +/** + * Lists the contents of a directory + * + * This version prepends each entry with the directory. + */ +pub fn list_dir_path(p: &Path) -> ~[~Path] { + list_dir(p).map(|f| ~p.push(*f)) +} + +/// Removes a directory at the specified path, after removing +/// all its contents. Use carefully! +pub fn remove_dir_recursive(p: &Path) -> bool { + let mut error_happened = false; + for walk_dir(p) |inner| { + if !error_happened { + if path_is_dir(inner) { + if !remove_dir_recursive(inner) { + error_happened = true; + } + } + else { + if !remove_file(inner) { + error_happened = true; + } + } + } + }; + // Directory should now be empty + !error_happened && remove_dir(p) +} + +/// Removes a directory at the specified path +pub fn remove_dir(p: &Path) -> bool { + return rmdir(p); + + #[cfg(windows)] + fn rmdir(p: &Path) -> bool { + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(p.to_str()) |buf| { + libc::RemoveDirectoryW(buf) != (0 as libc::BOOL) + }; + } + } + + #[cfg(unix)] + fn rmdir(p: &Path) -> bool { + unsafe { + return do as_c_charp(p.to_str()) |buf| { + libc::rmdir(buf) == (0 as c_int) + }; + } + } +} + +pub fn change_dir(p: &Path) -> bool { + return chdir(p); + + #[cfg(windows)] + fn chdir(p: &Path) -> bool { + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(p.to_str()) |buf| { + libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL) + }; + } + } + + #[cfg(unix)] + fn chdir(p: &Path) -> bool { + unsafe { + return do as_c_charp(p.to_str()) |buf| { + libc::chdir(buf) == (0 as c_int) + }; + } + } +} + +/// Changes the current working directory to the specified +/// path while acquiring a global lock, then calls `action`. +/// If the change is successful, releases the lock and restores the +/// CWD to what it was before, returning true. +/// Returns false if the directory doesn't exist or if the directory change +/// is otherwise unsuccessful. +pub fn change_dir_locked(p: &Path, action: &fn()) -> bool { + use unstable::global::global_data_clone_create; + use unstable::sync::{Exclusive, exclusive}; + + fn key(_: Exclusive<()>) { } + + let result = unsafe { + global_data_clone_create(key, || { + ~exclusive(()) + }) + }; + + do result.with_imm() |_| { + let old_dir = os::getcwd(); + if change_dir(p) { + action(); + change_dir(&old_dir) + } + else { + false + } + } +} + +/// Copies a file from one location to another +pub fn copy_file(from: &Path, to: &Path) -> bool { + return do_copy_file(from, to); + + #[cfg(windows)] + fn do_copy_file(from: &Path, to: &Path) -> bool { + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(from.to_str()) |fromp| { + do as_utf16_p(to.to_str()) |top| { + libc::CopyFileW(fromp, top, (0 as libc::BOOL)) != + (0 as libc::BOOL) + } + } + } + } + + #[cfg(unix)] + fn do_copy_file(from: &Path, to: &Path) -> bool { + unsafe { + let istream = do as_c_charp(from.to_str()) |fromp| { + do as_c_charp("rb") |modebuf| { + libc::fopen(fromp, modebuf) + } + }; + if istream as uint == 0u { + return false; + } + // Preserve permissions + let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \ + for source file"); + + let ostream = do as_c_charp(to.to_str()) |top| { + do as_c_charp("w+b") |modebuf| { + libc::fopen(top, modebuf) + } + }; + if ostream as uint == 0u { + fclose(istream); + return false; + } + let bufsize = 8192u; + let mut buf = vec::with_capacity::<u8>(bufsize); + let mut done = false; + let mut ok = true; + while !done { + do vec::as_mut_buf(buf) |b, _sz| { + let nread = libc::fread(b as *mut c_void, 1u as size_t, + bufsize as size_t, + istream); + if nread > 0 as size_t { + if libc::fwrite(b as *c_void, 1u as size_t, nread, + ostream) != nread { + ok = false; + done = true; + } + } else { + done = true; + } + } + } + fclose(istream); + fclose(ostream); + + // Give the new file the old file's permissions + unsafe { + if do str::as_c_str(to.to_str()) |to_buf| { + libc::chmod(to_buf, from_mode as mode_t) + } != 0 { + return false; // should be a condition... + } + } + return ok; + } + } +} + +/// Deletes an existing file +pub fn remove_file(p: &Path) -> bool { + return unlink(p); + + #[cfg(windows)] + fn unlink(p: &Path) -> bool { + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(p.to_str()) |buf| { + libc::DeleteFileW(buf) != (0 as libc::BOOL) + }; + } + } + + #[cfg(unix)] + fn unlink(p: &Path) -> bool { + unsafe { + return do as_c_charp(p.to_str()) |buf| { + libc::unlink(buf) == (0 as c_int) + }; + } + } +} + +#[cfg(unix)] +pub fn errno() -> int { + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn errno_location() -> *c_int { + #[nolink] + extern { + unsafe fn __error() -> *c_int; + } + unsafe { + __error() + } + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn errno_location() -> *c_int { + #[nolink] + extern { + unsafe fn __errno_location() -> *c_int; + } + unsafe { + __errno_location() + } + } + + unsafe { + (*errno_location()) as int + } +} + +#[cfg(windows)] +pub fn errno() -> uint { + use libc::types::os::arch::extra::DWORD; + + #[link_name = "kernel32"] + #[abi = "stdcall"] + extern "stdcall" { + unsafe fn GetLastError() -> DWORD; + } + + unsafe { + GetLastError() as uint + } +} + +/// Get a string representing the platform-dependent last error +pub fn last_os_error() -> ~str { + #[cfg(unix)] + fn strerror() -> ~str { + #[cfg(target_os = "macos")] + #[cfg(target_os = "android")] + #[cfg(target_os = "freebsd")] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + #[nolink] + extern { + unsafe fn strerror_r(errnum: c_int, buf: *mut c_char, + buflen: size_t) -> c_int; + } + unsafe { + strerror_r(errnum, buf, buflen) + } + } + + // GNU libc provides a non-compliant version of strerror_r by default + // and requires macros to instead use the POSIX compliant variant. + // So we just use __xpg_strerror_r which is always POSIX compliant + #[cfg(target_os = "linux")] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + #[nolink] + extern { + unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char, + buflen: size_t) -> c_int; + } + unsafe { + __xpg_strerror_r(errnum, buf, buflen) + } + } + + let mut buf = [0 as c_char, ..TMPBUF_SZ]; + unsafe { + let err = strerror_r(errno() as c_int, &mut buf[0], + TMPBUF_SZ as size_t); + if err < 0 { + fail!("strerror_r failure"); + } + + str::raw::from_c_str(&buf[0]) + } + } + + #[cfg(windows)] + fn strerror() -> ~str { + use libc::types::os::arch::extra::DWORD; + use libc::types::os::arch::extra::LPSTR; + use libc::types::os::arch::extra::LPVOID; + + #[link_name = "kernel32"] + #[abi = "stdcall"] + extern "stdcall" { + unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID, + msgId: DWORD, langId: DWORD, + buf: LPSTR, nsize: DWORD, + args: *c_void) -> DWORD; + } + + static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000; + static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200; + + let mut buf = [0 as c_char, ..TMPBUF_SZ]; + + // This value is calculated from the macro + // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) + let langId = 0x0800 as DWORD; + let err = errno() as DWORD; + unsafe { + let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + ptr::mut_null(), err, langId, + &mut buf[0], TMPBUF_SZ as DWORD, + ptr::null()); + if res == 0 { + fail!("[%?] FormatMessage failure", errno()); + } + + str::raw::from_c_str(&buf[0]) + } + } + + strerror() +} + +/** + * Sets the process exit code + * + * Sets the exit code returned by the process if all supervised tasks + * terminate successfully (without failing). If the current root task fails + * and is supervised by the scheduler then any user-specified exit status is + * ignored and the process exits with the default failure status + */ +pub fn set_exit_status(code: int) { + unsafe { + rustrt::rust_set_exit_status(code as libc::intptr_t); + } +} + +unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] { + let mut args = ~[]; + for uint::range(0, argc as uint) |i| { + vec::push(&mut args, str::raw::from_c_str(*argv.offset(i))); + } + args +} + +/** + * Returns the command line arguments + * + * Returns a list of the command line arguments. + */ +#[cfg(target_os = "macos")] +pub fn real_args() -> ~[~str] { + unsafe { + let (argc, argv) = (*_NSGetArgc() as c_int, + *_NSGetArgv() as **c_char); + load_argc_and_argv(argc, argv) + } +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +#[cfg(target_os = "freebsd")] +pub fn real_args() -> ~[~str] { + unsafe { + let argc = rustrt::rust_get_argc(); + let argv = rustrt::rust_get_argv(); + load_argc_and_argv(argc, argv) + } +} + +#[cfg(windows)] +pub fn real_args() -> ~[~str] { + let mut nArgs: c_int = 0; + let lpArgCount = ptr::to_mut_unsafe_ptr(&mut nArgs); + let lpCmdLine = unsafe { GetCommandLineW() }; + let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) }; + + let mut args = ~[]; + for uint::range(0, nArgs as uint) |i| { + unsafe { + // Determine the length of this argument. + let ptr = *szArgList.offset(i); + let mut len = 0; + while *ptr.offset(len) != 0 { len += 1; } + + // Push it onto the list. + vec::push(&mut args, + vec::raw::buf_as_slice(ptr, len, + str::from_utf16)); + } + } + + unsafe { + LocalFree(cast::transmute(szArgList)); + } + + return args; +} + +type LPCWSTR = *u16; + +#[cfg(windows)] +#[link_name="kernel32"] +#[abi="stdcall"] +extern "stdcall" { + fn GetCommandLineW() -> LPCWSTR; + fn LocalFree(ptr: *c_void); +} + +#[cfg(windows)] +#[link_name="shell32"] +#[abi="stdcall"] +extern "stdcall" { + fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16; +} + +struct OverriddenArgs { + val: ~[~str] +} + +fn overridden_arg_key(_v: @OverriddenArgs) {} + +pub fn args() -> ~[~str] { + unsafe { + match local_data::local_data_get(overridden_arg_key) { + None => real_args(), + Some(args) => copy args.val + } + } +} + +pub fn set_args(new_args: ~[~str]) { + unsafe { + let overridden_args = @OverriddenArgs { val: copy new_args }; + local_data::local_data_set(overridden_arg_key, overridden_args); + } +} + +// FIXME #6100 we should really use an internal implementation of this - using +// the POSIX glob functions isn't portable to windows, probably has slight +// inconsistencies even where it is implemented, and makes extending +// functionality a lot more difficult +// FIXME #6101 also provide a non-allocating version - each_glob or so? +/// Returns a vector of Path objects that match the given glob pattern +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "macos")] +pub fn glob(pattern: &str) -> ~[Path] { + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn default_glob_t () -> libc::glob_t { + libc::glob_t { + gl_pathc: 0, + gl_pathv: ptr::null(), + gl_offs: 0, + __unused1: ptr::null(), + __unused2: ptr::null(), + __unused3: ptr::null(), + __unused4: ptr::null(), + __unused5: ptr::null(), + } + } + + #[cfg(target_os = "freebsd")] + fn default_glob_t () -> libc::glob_t { + libc::glob_t { + gl_pathc: 0, + __unused1: 0, + gl_offs: 0, + __unused2: 0, + gl_pathv: ptr::null(), + __unused3: ptr::null(), + __unused4: ptr::null(), + __unused5: ptr::null(), + __unused6: ptr::null(), + __unused7: ptr::null(), + __unused8: ptr::null(), + } + } + + #[cfg(target_os = "macos")] + fn default_glob_t () -> libc::glob_t { + libc::glob_t { + gl_pathc: 0, + __unused1: 0, + gl_offs: 0, + __unused2: 0, + gl_pathv: ptr::null(), + __unused3: ptr::null(), + __unused4: ptr::null(), + __unused5: ptr::null(), + __unused6: ptr::null(), + __unused7: ptr::null(), + __unused8: ptr::null(), + } + } + + let mut g = default_glob_t(); + do str::as_c_str(pattern) |c_pattern| { + unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) } + }; + do(|| { + let paths = unsafe { + vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint) + }; + do paths.map |&c_str| { + Path(unsafe { str::raw::from_c_str(c_str) }) + } + }).finally { + unsafe { libc::globfree(&mut g) }; + } +} + +/// Returns a vector of Path objects that match the given glob pattern +#[cfg(target_os = "win32")] +pub fn glob(pattern: &str) -> ~[Path] { + fail!("glob() is unimplemented on Windows") +} + +#[cfg(target_os = "macos")] +extern { + // These functions are in crt_externs.h. + pub fn _NSGetArgc() -> *c_int; + pub fn _NSGetArgv() -> ***c_char; +} + +pub mod consts { + + #[cfg(unix)] + pub use os::consts::unix::*; + + #[cfg(windows)] + pub use os::consts::windows::*; + + #[cfg(target_os = "macos")] + pub use os::consts::macos::*; + + #[cfg(target_os = "freebsd")] + pub use os::consts::freebsd::*; + + #[cfg(target_os = "linux")] + pub use os::consts::linux::*; + + #[cfg(target_os = "android")] + pub use os::consts::android::*; + + #[cfg(target_os = "win32")] + pub use os::consts::win32::*; + + #[cfg(target_arch = "x86")] + pub use os::consts::x86::*; + + #[cfg(target_arch = "x86_64")] + pub use os::consts::x86_64::*; + + #[cfg(target_arch = "arm")] + pub use os::consts::arm::*; + + #[cfg(target_arch = "mips")] + use os::consts::mips::*; + + pub mod unix { + pub static FAMILY: &'static str = "unix"; + } + + pub mod windows { + pub static FAMILY: &'static str = "windows"; + } + + pub mod macos { + pub static SYSNAME: &'static str = "macos"; + pub static DLL_PREFIX: &'static str = "lib"; + pub static DLL_SUFFIX: &'static str = ".dylib"; + pub static EXE_SUFFIX: &'static str = ""; + } + + pub mod freebsd { + pub static SYSNAME: &'static str = "freebsd"; + pub static DLL_PREFIX: &'static str = "lib"; + pub static DLL_SUFFIX: &'static str = ".so"; + pub static EXE_SUFFIX: &'static str = ""; + } + + pub mod linux { + pub static SYSNAME: &'static str = "linux"; + pub static DLL_PREFIX: &'static str = "lib"; + pub static DLL_SUFFIX: &'static str = ".so"; + pub static EXE_SUFFIX: &'static str = ""; + } + + pub mod android { + pub static SYSNAME: &'static str = "android"; + pub static DLL_PREFIX: &'static str = "lib"; + pub static DLL_SUFFIX: &'static str = ".so"; + pub static EXE_SUFFIX: &'static str = ""; + } + + pub mod win32 { + pub static SYSNAME: &'static str = "win32"; + pub static DLL_PREFIX: &'static str = ""; + pub static DLL_SUFFIX: &'static str = ".dll"; + pub static EXE_SUFFIX: &'static str = ".exe"; + } + + + pub mod x86 { + pub static ARCH: &'static str = "x86"; + } + pub mod x86_64 { + pub static ARCH: &'static str = "x86_64"; + } + pub mod arm { + pub static ARCH: &'static str = "arm"; + } + pub mod mips { + pub static ARCH: &'static str = "mips"; + } +} + +#[cfg(test)] +#[allow(non_implicitly_copyable_typarams)] +mod tests { + use libc::{c_int, c_void, size_t}; + use libc; + use option::Some; + use option; + use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args}; + use os::{remove_file, setenv, unsetenv}; + use os; + use path::Path; + use rand::RngUtil; + use rand; + use run; + use str; + use vec; + use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; + + + #[test] + pub fn last_os_error() { + debug!(os::last_os_error()); + } + + #[test] + pub fn test_args() { + let a = real_args(); + assert!(a.len() >= 1); + } + + fn make_rand_name() -> ~str { + let mut rng = rand::rng(); + let n = ~"TEST" + rng.gen_str(10u); + assert!(getenv(n).is_none()); + n + } + + #[test] + fn test_setenv() { + let n = make_rand_name(); + setenv(n, ~"VALUE"); + assert_eq!(getenv(n), option::Some(~"VALUE")); + } + + #[test] + fn test_unsetenv() { + let n = make_rand_name(); + setenv(n, ~"VALUE"); + unsetenv(n); + assert_eq!(getenv(n), option::None); + } + + #[test] + #[ignore(cfg(windows))] + #[ignore] + fn test_setenv_overwrite() { + let n = make_rand_name(); + setenv(n, ~"1"); + setenv(n, ~"2"); + assert_eq!(getenv(n), option::Some(~"2")); + setenv(n, ~""); + assert_eq!(getenv(n), option::Some(~"")); + } + + // Windows GetEnvironmentVariable requires some extra work to make sure + // the buffer the variable is copied into is the right size + #[test] + #[ignore(cfg(windows))] + #[ignore] + fn test_getenv_big() { + let mut s = ~""; + let mut i = 0; + while i < 100 { s += ~"aaaaaaaaaa"; i += 1; } + let n = make_rand_name(); + setenv(n, s); + debug!(copy s); + assert_eq!(getenv(n), option::Some(s)); + } + + #[test] + fn test_self_exe_path() { + let path = os::self_exe_path(); + assert!(path.is_some()); + let path = path.get(); + debug!(copy path); + + // Hard to test this function + assert!(path.is_absolute); + } + + #[test] + #[ignore] + fn test_env_getenv() { + let e = env(); + assert!(e.len() > 0u); + for e.each |p| { + let (n, v) = copy *p; + debug!(copy n); + let v2 = getenv(n); + // MingW seems to set some funky environment variables like + // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned + // from env() but not visible from getenv(). + assert!(v2.is_none() || v2 == option::Some(v)); + } + } + + #[test] + fn test_env_setenv() { + let n = make_rand_name(); + + let mut e = env(); + setenv(n, ~"VALUE"); + assert!(!vec::contains(e, &(copy n, ~"VALUE"))); + + e = env(); + assert!(vec::contains(e, &(n, ~"VALUE"))); + } + + #[test] + fn test() { + assert!((!Path("test-path").is_absolute)); + + debug!(~"Current working directory: " + getcwd().to_str()); + + debug!(make_absolute(&Path("test-path"))); + debug!(make_absolute(&Path("/usr/bin"))); + } + + #[test] + #[cfg(unix)] + fn homedir() { + let oldhome = getenv(~"HOME"); + + setenv(~"HOME", ~"/home/MountainView"); + assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + + setenv(~"HOME", ~""); + assert!(os::homedir().is_none()); + + for oldhome.each |s| { setenv(~"HOME", *s) } + } + + #[test] + #[cfg(windows)] + fn homedir() { + + let oldhome = getenv(~"HOME"); + let olduserprofile = getenv(~"USERPROFILE"); + + setenv(~"HOME", ~""); + setenv(~"USERPROFILE", ~""); + + assert!(os::homedir().is_none()); + + setenv(~"HOME", ~"/home/MountainView"); + assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + + setenv(~"HOME", ~""); + + setenv(~"USERPROFILE", ~"/home/MountainView"); + assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + + setenv(~"HOME", ~"/home/MountainView"); + setenv(~"USERPROFILE", ~"/home/PaloAlto"); + assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + + oldhome.each(|s| {setenv(~"HOME", *s);true}); + olduserprofile.each(|s| {setenv(~"USERPROFILE", *s);true}); + } + + #[test] + fn tmpdir() { + assert!(!str::is_empty(os::tmpdir().to_str())); + } + + // Issue #712 + #[test] + fn test_list_dir_no_invalid_memory_access() { + os::list_dir(&Path(".")); + } + + #[test] + fn list_dir() { + let dirs = os::list_dir(&Path(".")); + // Just assuming that we've got some contents in the current directory + assert!(dirs.len() > 0u); + + for dirs.each |dir| { + debug!(copy *dir); + } + } + + #[test] + fn path_is_dir() { + assert!((os::path_is_dir(&Path(".")))); + assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs")))); + } + + #[test] + fn path_exists() { + assert!((os::path_exists(&Path(".")))); + assert!((!os::path_exists(&Path( + "test/nonexistent-bogus-path")))); + } + + #[test] + fn copy_file_does_not_exist() { + assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"), + &Path("test/other-bogus-path"))); + assert!(!os::path_exists(&Path("test/other-bogus-path"))); + } + + #[test] + fn copy_file_ok() { + unsafe { + let tempdir = getcwd(); // would like to use $TMPDIR, + // doesn't seem to work on Linux + assert!((str::len(tempdir.to_str()) > 0u)); + let in = tempdir.push("in.txt"); + let out = tempdir.push("out.txt"); + + /* Write the temp input file */ + let ostream = do as_c_charp(in.to_str()) |fromp| { + do as_c_charp("w+b") |modebuf| { + libc::fopen(fromp, modebuf) + } + }; + assert!((ostream as uint != 0u)); + let s = ~"hello"; + let mut buf = str::to_bytes(s) + ~[0 as u8]; + do vec::as_mut_buf(buf) |b, _len| { + assert!((libc::fwrite(b as *c_void, 1u as size_t, + (str::len(s) + 1u) as size_t, ostream) + == buf.len() as size_t)) + } + assert_eq!(libc::fclose(ostream), (0u as c_int)); + let in_mode = in.get_mode(); + let rs = os::copy_file(&in, &out); + if (!os::path_exists(&in)) { + fail!("%s doesn't exist", in.to_str()); + } + assert!((rs)); + let rslt = run::run_program(~"diff", ~[in.to_str(), out.to_str()]); + assert_eq!(rslt, 0); + assert_eq!(out.get_mode(), in_mode); + assert!((remove_file(&in))); + assert!((remove_file(&out))); + } + } + + #[test] + fn recursive_mkdir_slash() { + let path = Path("/"); + assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); + } + + #[test] + fn recursive_mkdir_empty() { + let path = Path(""); + assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); + } + + // More recursive_mkdir tests are in std::tempfile +} diff --git a/src/libstd/owned.rs b/src/libstd/owned.rs new file mode 100644 index 00000000000..3262fb4afdc --- /dev/null +++ b/src/libstd/owned.rs @@ -0,0 +1,33 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations on unique pointer types + +#[cfg(not(test))] use cmp::{Eq, Ord}; + +#[cfg(not(test))] +impl<T:Eq> Eq for ~T { + #[inline(always)] + fn eq(&self, other: &~T) -> bool { *(*self) == *(*other) } + #[inline(always)] + fn ne(&self, other: &~T) -> bool { *(*self) != *(*other) } +} + +#[cfg(not(test))] +impl<T:Ord> Ord for ~T { + #[inline(always)] + fn lt(&self, other: &~T) -> bool { *(*self) < *(*other) } + #[inline(always)] + fn le(&self, other: &~T) -> bool { *(*self) <= *(*other) } + #[inline(always)] + fn ge(&self, other: &~T) -> bool { *(*self) >= *(*other) } + #[inline(always)] + fn gt(&self, other: &~T) -> bool { *(*self) > *(*other) } +} diff --git a/src/libstd/par.rs b/src/libstd/par.rs deleted file mode 100644 index e766d2d3bf0..00000000000 --- a/src/libstd/par.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 future_spawn = future::spawn; - -/** - * The maximum number of tasks this module will spawn for a single - * operation. - */ -static max_tasks : uint = 32u; - -/// The minimum number of elements each task will process. -static min_granularity : uint = 1024u; - -/** - * An internal helper to map a function over a large vector and - * return the intermediate results. - * - * This is used to build most of the other parallel vector functions, - * like map or alli. - */ -fn map_slices<A:Copy + Owned,B:Copy + Owned>( - xs: &[A], - f: &fn() -> ~fn(uint, v: &[A]) -> B) - -> ~[B] { - - let len = xs.len(); - if len < min_granularity { - info!("small slice"); - // This is a small vector, fall back on the normal map. - ~[f()(0u, xs)] - } - else { - let num_tasks = uint::min(max_tasks, len / min_granularity); - - let items_per_task = len / num_tasks; - - let mut futures = ~[]; - let mut base = 0u; - info!("spawning tasks"); - while base < len { - let end = uint::min(len, base + items_per_task); - do vec::as_imm_buf(xs) |p, _len| { - let f = f(); - let base = base; - let f = do future_spawn() || { - unsafe { - let len = end - base; - let slice = (ptr::offset(p, base), - len * sys::size_of::<A>()); - info!("pre-slice: %?", (base, slice)); - let slice : &[A] = - cast::transmute(slice); - info!("slice: %?", (base, slice.len(), end - base)); - assert_eq!(slice.len(), end - base); - f(base, slice) - } - }; - futures.push(f); - }; - base += items_per_task; - } - info!("tasks spawned"); - - info!("num_tasks: %?", (num_tasks, futures.len())); - assert_eq!(num_tasks, futures.len()); - - let r = do vec::map_consume(futures) |ys| { - let mut ys = ys; - ys.get() - }; - r - } -} - -/// A parallel version of map. -pub fn map<A:Copy + Owned,B:Copy + Owned>( - xs: &[A], fn_factory: &fn() -> ~fn(&A) -> B) -> ~[B] { - vec::concat(map_slices(xs, || { - let f = fn_factory(); - let result: ~fn(uint, &[A]) -> ~[B] = - |_, slice| vec::map(slice, |x| f(x)); - result - })) -} - -/// A parallel version of mapi. -pub fn mapi<A:Copy + Owned,B:Copy + Owned>( - xs: &[A], - fn_factory: &fn() -> ~fn(uint, &A) -> B) -> ~[B] { - let slices = map_slices(xs, || { - let f = fn_factory(); - let result: ~fn(uint, &[A]) -> ~[B] = |base, slice| { - vec::mapi(slice, |i, x| { - f(i + base, x) - }) - }; - result - }); - let r = vec::concat(slices); - info!("%?", (r.len(), xs.len())); - assert_eq!(r.len(), xs.len()); - r -} - -/// Returns true if the function holds for all elements in the vector. -pub fn alli<A:Copy + Owned>( - xs: &[A], - fn_factory: &fn() -> ~fn(uint, &A) -> bool) -> bool -{ - do vec::all(map_slices(xs, || { - let f = fn_factory(); - let result: ~fn(uint, &[A]) -> bool = |base, slice| { - vec::alli(slice, |i, x| { - f(i + base, x) - }) - }; - result - })) |x| { *x } -} - -/// Returns true if the function holds for any elements in the vector. -pub fn any<A:Copy + Owned>( - xs: &[A], - fn_factory: &fn() -> ~fn(&A) -> bool) -> bool { - do vec::any(map_slices(xs, || { - let f = fn_factory(); - let result: ~fn(uint, &[A]) -> bool = - |_, slice| vec::any(slice, |x| f(x)); - result - })) |x| { *x } -} diff --git a/src/libstd/path.rs b/src/libstd/path.rs new file mode 100644 index 00000000000..ed9ef864f80 --- /dev/null +++ b/src/libstd/path.rs @@ -0,0 +1,1200 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Cross-platform file path handling + +*/ + +use container::Container; +use cmp::Eq; +use libc; +use option::{None, Option, Some}; +use str; +use str::StrSlice; +use to_str::ToStr; +use ascii::{AsciiCast, AsciiStr}; +use old_iter::BaseIter; +use vec::OwnedVector; + +#[cfg(windows)] +pub use Path = self::WindowsPath; +#[cfg(unix)] +pub use Path = self::PosixPath; + +#[deriving(Clone, Eq)] +pub struct WindowsPath { + host: Option<~str>, + device: Option<~str>, + is_absolute: bool, + components: ~[~str], +} + +pub fn WindowsPath(s: &str) -> WindowsPath { + GenericPath::from_str(s) +} + +#[deriving(Clone, Eq)] +pub struct PosixPath { + is_absolute: bool, + components: ~[~str], +} + +pub fn PosixPath(s: &str) -> PosixPath { + GenericPath::from_str(s) +} + +pub trait GenericPath { + /// Converts a string to a Path + fn from_str(&str) -> Self; + + /// Returns the directory component of `self`, as a string + fn dirname(&self) -> ~str; + /// Returns the file component of `self`, as a string option. + /// Returns None if `self` names a directory. + fn filename(&self) -> Option<~str>; + /// Returns the stem of the file component of `self`, as a string option. + /// The stem is the slice of a filename starting at 0 and ending just before + /// the last '.' in the name. + /// Returns None if `self` names a directory. + fn filestem(&self) -> Option<~str>; + /// Returns the type of the file component of `self`, as a string option. + /// The file type is the slice of a filename starting just after the last + /// '.' in the name and ending at the last index in the filename. + /// Returns None if `self` names a directory. + fn filetype(&self) -> Option<~str>; + + /// Returns a new path consisting of `self` with the parent directory component replaced + /// with the given string. + fn with_dirname(&self, (&str)) -> Self; + /// Returns a new path consisting of `self` with the file component replaced + /// with the given string. + fn with_filename(&self, (&str)) -> Self; + /// Returns a new path consisting of `self` with the file stem replaced + /// with the given string. + fn with_filestem(&self, (&str)) -> Self; + /// Returns a new path consisting of `self` with the file type replaced + /// with the given string. + fn with_filetype(&self, (&str)) -> Self; + + /// Returns the directory component of `self`, as a new path. + /// If `self` has no parent, returns `self`. + fn dir_path(&self) -> Self; + /// Returns the file component of `self`, as a new path. + /// If `self` names a directory, returns the empty path. + fn file_path(&self) -> Self; + + /// Returns a new Path whose parent directory is `self` and whose + /// file component is the given string. + fn push(&self, (&str)) -> Self; + /// Returns a new Path consisting of the given path, made relative to `self`. + fn push_rel(&self, (&Self)) -> Self; + /// Returns a new Path consisting of the path given by the given vector + /// of strings, relative to `self`. + fn push_many(&self, (&[~str])) -> Self; + /// Identical to `dir_path` except in the case where `self` has only one + /// component. In this case, `pop` returns the empty path. + fn pop(&self) -> Self; + + /// The same as `push_rel`, except that the directory argument must not + /// contain directory separators in any of its components. + fn unsafe_join(&self, (&Self)) -> Self; + /// On Unix, always returns false. On Windows, returns true iff `self`'s + /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4` + /// `lpt1` `lpt2` `lpt3` `prn` `nul` + fn is_restricted(&self) -> bool; + + /// Returns a new path that names the same file as `self`, without containing + /// any '.', '..', or empty components. On Windows, uppercases the drive letter + /// as well. + fn normalize(&self) -> Self; + + /// Returns `true` if `self` is an absolute path. + fn is_absolute(&self) -> bool; +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +mod stat { + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + __pad1: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + __pad2: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + __unused4: 0, + __unused5: 0, + } + } + } + + #[cfg(target_arch = "mips")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_pad1: [0, ..3], + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_pad2: [0, ..2], + st_size: 0, + st_pad3: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_blksize: 0, + st_blocks: 0, + st_pad5: [0, ..14], + } + } + } + + #[cfg(target_arch = "x86_64")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_nlink: 0, + st_mode: 0, + st_uid: 0, + st_gid: 0, + __pad0: 0, + st_rdev: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + __unused: [0, 0, 0], + } + } + } +} + +#[cfg(target_os = "freebsd")] +mod stat { + #[cfg(target_arch = "x86_64")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_size: 0, + st_blocks: 0, + st_blksize: 0, + st_flags: 0, + st_gen: 0, + st_lspare: 0, + st_birthtime: 0, + st_birthtime_nsec: 0, + __unused: [0, 0], + } + } + } +} + +#[cfg(target_os = "macos")] +mod stat { + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_mode: 0, + st_nlink: 0, + st_ino: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_birthtime: 0, + st_birthtime_nsec: 0, + st_size: 0, + st_blocks: 0, + st_blksize: 0, + st_flags: 0, + st_gen: 0, + st_lspare: 0, + st_qspare: [0, 0], + } + } + } +} + +#[cfg(target_os = "win32")] +mod stat { + pub mod arch { + use libc; + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_size: 0, + st_atime: 0, + st_mtime: 0, + st_ctime: 0, + } + } + } +} + + +pub impl Path { + fn stat(&self) -> Option<libc::stat> { + unsafe { + do str::as_c_str(self.to_str()) |buf| { + let mut st = stat::arch::default_stat(); + match libc::stat(buf, &mut st) { + 0 => Some(st), + _ => None, + } + } + } + } + + #[cfg(unix)] + fn lstat(&self) -> Option<libc::stat> { + unsafe { + do str::as_c_str(self.to_str()) |buf| { + let mut st = stat::arch::default_stat(); + match libc::lstat(buf, &mut st) { + 0 => Some(st), + _ => None, + } + } + } + } + + fn exists(&self) -> bool { + match self.stat() { + None => false, + Some(_) => true, + } + } + + fn get_size(&self) -> Option<i64> { + match self.stat() { + None => None, + Some(ref st) => Some(st.st_size as i64), + } + } + + fn get_mode(&self) -> Option<uint> { + match self.stat() { + None => None, + Some(ref st) => Some(st.st_mode as uint), + } + } +} + +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +pub impl Path { + fn get_atime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_atime as i64, + st.st_atime_nsec as int)) + } + } + } + + fn get_mtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_mtime as i64, + st.st_mtime_nsec as int)) + } + } + } + + fn get_ctime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_ctime as i64, + st.st_ctime_nsec as int)) + } + } + } +} + +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "macos")] +pub impl Path { + fn get_birthtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_birthtime as i64, + st.st_birthtime_nsec as int)) + } + } + } +} + +#[cfg(target_os = "win32")] +pub impl Path { + fn get_atime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_atime as i64, 0)) + } + } + } + + fn get_mtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_mtime as i64, 0)) + } + } + } + + fn get_ctime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(ref st) => { + Some((st.st_ctime as i64, 0)) + } + } + } +} + +impl ToStr for PosixPath { + fn to_str(&self) -> ~str { + let mut s = ~""; + if self.is_absolute { + s += "/"; + } + s + str::connect(self.components, "/") + } +} + +// FIXME (#3227): when default methods in traits are working, de-duplicate +// PosixPath and WindowsPath, most of their methods are common. +impl GenericPath for PosixPath { + fn from_str(s: &str) -> PosixPath { + let mut components = ~[]; + for str::each_split_nonempty(s, |c| c == '/') |s| { + components.push(s.to_owned()) + } + let is_absolute = (s.len() != 0 && s[0] == '/' as u8); + PosixPath { + is_absolute: is_absolute, + components: components, + } + } + + fn dirname(&self) -> ~str { + let s = self.dir_path().to_str(); + match s.len() { + 0 => ~".", + _ => s, + } + } + + fn filename(&self) -> Option<~str> { + match self.components.len() { + 0 => None, + n => Some(copy self.components[n - 1]), + } + } + + fn filestem(&self) -> Option<~str> { + match self.filename() { + None => None, + Some(ref f) => { + match str::rfind_char(*f, '.') { + Some(p) => Some(f.slice(0, p).to_owned()), + None => Some(copy *f), + } + } + } + } + + fn filetype(&self) -> Option<~str> { + match self.filename() { + None => None, + Some(ref f) => { + match str::rfind_char(*f, '.') { + Some(p) if p < f.len() => Some(f.slice(p, f.len()).to_owned()), + _ => None, + } + } + } + } + + fn with_dirname(&self, d: &str) -> PosixPath { + let dpath = PosixPath(d); + match self.filename() { + Some(ref f) => dpath.push(*f), + None => dpath, + } + } + + fn with_filename(&self, f: &str) -> PosixPath { + assert!(! str::any(f, |c| windows::is_sep(c as u8))); + self.dir_path().push(f) + } + + fn with_filestem(&self, s: &str) -> PosixPath { + match self.filetype() { + None => self.with_filename(s), + Some(ref t) => self.with_filename(str::to_owned(s) + *t), + } + } + + fn with_filetype(&self, t: &str) -> PosixPath { + match (t.len(), self.filestem()) { + (0, None) => copy *self, + (0, Some(ref s)) => self.with_filename(*s), + (_, None) => self.with_filename(fmt!(".%s", t)), + (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)), + } + } + + fn dir_path(&self) -> PosixPath { + match self.components.len() { + 0 => copy *self, + _ => self.pop(), + } + } + + fn file_path(&self) -> PosixPath { + let cs = match self.filename() { + None => ~[], + Some(ref f) => ~[copy *f] + }; + PosixPath { + is_absolute: false, + components: cs, + } + } + + fn push_rel(&self, other: &PosixPath) -> PosixPath { + assert!(!other.is_absolute); + self.push_many(other.components) + } + + fn unsafe_join(&self, other: &PosixPath) -> PosixPath { + if other.is_absolute { + PosixPath { + is_absolute: true, + components: copy other.components, + } + } else { + self.push_rel(other) + } + } + + fn is_restricted(&self) -> bool { + false + } + + fn push_many(&self, cs: &[~str]) -> PosixPath { + let mut v = copy self.components; + for cs.each |e| { + let mut ss = ~[]; + for str::each_split_nonempty(*e, |c| windows::is_sep(c as u8)) |s| { + ss.push(s.to_owned()) + } + v.push_all_move(ss); + } + PosixPath { + is_absolute: self.is_absolute, + components: v, + } + } + + fn push(&self, s: &str) -> PosixPath { + let mut v = copy self.components; + let mut ss = ~[]; + for str::each_split_nonempty(s, |c| windows::is_sep(c as u8)) |s| { + ss.push(s.to_owned()) + } + v.push_all_move(ss); + PosixPath { components: v, ..copy *self } + } + + fn pop(&self) -> PosixPath { + let mut cs = copy self.components; + if cs.len() != 0 { + cs.pop(); + } + PosixPath { + is_absolute: self.is_absolute, + components: cs, + } //..self } + } + + fn normalize(&self) -> PosixPath { + PosixPath { + is_absolute: self.is_absolute, + components: normalize(self.components), + } // ..self } + } + + fn is_absolute(&self) -> bool { + self.is_absolute + } +} + + +impl ToStr for WindowsPath { + fn to_str(&self) -> ~str { + let mut s = ~""; + match self.host { + Some(ref h) => { s += "\\\\"; s += *h; } + None => { } + } + match self.device { + Some(ref d) => { s += *d; s += ":"; } + None => { } + } + if self.is_absolute { + s += "\\"; + } + s + str::connect(self.components, "\\") + } +} + + +impl GenericPath for WindowsPath { + fn from_str(s: &str) -> WindowsPath { + let host; + let device; + let rest; + + match ( + windows::extract_drive_prefix(s), + windows::extract_unc_prefix(s), + ) { + (Some((ref d, ref r)), _) => { + host = None; + device = Some(copy *d); + rest = copy *r; + } + (None, Some((ref h, ref r))) => { + host = Some(copy *h); + device = None; + rest = copy *r; + } + (None, None) => { + host = None; + device = None; + rest = str::to_owned(s); + } + } + + let mut components = ~[]; + for str::each_split_nonempty(rest, |c| windows::is_sep(c as u8)) |s| { + components.push(s.to_owned()) + } + let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0])); + WindowsPath { + host: host, + device: device, + is_absolute: is_absolute, + components: components, + } + } + + fn dirname(&self) -> ~str { + let s = self.dir_path().to_str(); + match s.len() { + 0 => ~".", + _ => s, + } + } + + fn filename(&self) -> Option<~str> { + match self.components.len() { + 0 => None, + n => Some(copy self.components[n - 1]), + } + } + + fn filestem(&self) -> Option<~str> { + match self.filename() { + None => None, + Some(ref f) => { + match str::rfind_char(*f, '.') { + Some(p) => Some(f.slice(0, p).to_owned()), + None => Some(copy *f), + } + } + } + } + + fn filetype(&self) -> Option<~str> { + match self.filename() { + None => None, + Some(ref f) => { + match str::rfind_char(*f, '.') { + Some(p) if p < f.len() => Some(f.slice(p, f.len()).to_owned()), + _ => None, + } + } + } + } + + fn with_dirname(&self, d: &str) -> WindowsPath { + let dpath = WindowsPath(d); + match self.filename() { + Some(ref f) => dpath.push(*f), + None => dpath, + } + } + + fn with_filename(&self, f: &str) -> WindowsPath { + assert!(! str::any(f, |c| windows::is_sep(c as u8))); + self.dir_path().push(f) + } + + fn with_filestem(&self, s: &str) -> WindowsPath { + match self.filetype() { + None => self.with_filename(s), + Some(ref t) => self.with_filename(str::to_owned(s) + *t), + } + } + + fn with_filetype(&self, t: &str) -> WindowsPath { + match (t.len(), self.filestem()) { + (0, None) => copy *self, + (0, Some(ref s)) => self.with_filename(*s), + (_, None) => self.with_filename(fmt!(".%s", t)), + (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)), + } + } + + fn dir_path(&self) -> WindowsPath { + match self.components.len() { + 0 => copy *self, + _ => self.pop(), + } + } + + fn file_path(&self) -> WindowsPath { + WindowsPath { + host: None, + device: None, + is_absolute: false, + components: match self.filename() { + None => ~[], + Some(ref f) => ~[copy *f], + } + } + } + + fn push_rel(&self, other: &WindowsPath) -> WindowsPath { + assert!(!other.is_absolute); + self.push_many(other.components) + } + + fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath { + /* rhs not absolute is simple push */ + if !other.is_absolute { + return self.push_many(other.components); + } + + /* if rhs has a host set, then the whole thing wins */ + match other.host { + Some(copy host) => { + return WindowsPath { + host: Some(host), + device: copy other.device, + is_absolute: true, + components: copy other.components, + }; + } + _ => {} + } + + /* if rhs has a device set, then a part wins */ + match other.device { + Some(copy device) => { + return WindowsPath { + host: None, + device: Some(device), + is_absolute: true, + components: copy other.components, + }; + } + _ => {} + } + + /* fallback: host and device of lhs win, but the + whole path of the right */ + WindowsPath { + host: copy self.host, + device: copy self.device, + is_absolute: self.is_absolute || other.is_absolute, + components: copy other.components, + } + } + + fn is_restricted(&self) -> bool { + match self.filestem() { + Some(stem) => { + // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use + // to_ascii_consume and to_str_consume to not do a unnecessary copy. + match stem.to_ascii().to_lower().to_str_ascii() { + ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" | + ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true, + _ => false + } + }, + None => false + } + } + + fn push_many(&self, cs: &[~str]) -> WindowsPath { + let mut v = copy self.components; + for cs.each |e| { + let mut ss = ~[]; + for str::each_split_nonempty(*e, |c| windows::is_sep(c as u8)) |s| { + ss.push(s.to_owned()) + } + v.push_all_move(ss); + } + // tedious, but as-is, we can't use ..self + WindowsPath { + host: copy self.host, + device: copy self.device, + is_absolute: self.is_absolute, + components: v + } + } + + fn push(&self, s: &str) -> WindowsPath { + let mut v = copy self.components; + let mut ss = ~[]; + for str::each_split_nonempty(s, |c| windows::is_sep(c as u8)) |s| { + ss.push(s.to_owned()) + } + v.push_all_move(ss); + WindowsPath { components: v, ..copy *self } + } + + fn pop(&self) -> WindowsPath { + let mut cs = copy self.components; + if cs.len() != 0 { + cs.pop(); + } + WindowsPath { + host: copy self.host, + device: copy self.device, + is_absolute: self.is_absolute, + components: cs, + } + } + + fn normalize(&self) -> WindowsPath { + WindowsPath { + host: copy self.host, + device: match self.device { + None => None, + + // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use + // to_ascii_consume and to_str_consume to not do a unnecessary copy. + Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii()) + }, + is_absolute: self.is_absolute, + components: normalize(self.components) + } + } + + fn is_absolute(&self) -> bool { + self.is_absolute + } +} + + +pub fn normalize(components: &[~str]) -> ~[~str] { + let mut cs = ~[]; + for components.each |c| { + if *c == ~"." && components.len() > 1 { loop; } + if *c == ~"" { loop; } + if *c == ~".." && cs.len() != 0 { + cs.pop(); + loop; + } + cs.push(copy *c); + } + cs +} + +// Various windows helpers, and tests for the impl. +pub mod windows { + use libc; + use option::{None, Option, Some}; + + #[inline(always)] + pub fn is_sep(u: u8) -> bool { + u == '/' as u8 || u == '\\' as u8 + } + + pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> { + if (s.len() > 1 && + (s[0] == '\\' as u8 || s[0] == '/' as u8) && + s[0] == s[1]) { + let mut i = 2; + while i < s.len() { + if is_sep(s[i]) { + let pre = s.slice(2, i).to_owned(); + let rest = s.slice(i, s.len()).to_owned(); + return Some((pre, rest)); + } + i += 1; + } + } + None + } + + pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> { + unsafe { + if (s.len() > 1 && + libc::isalpha(s[0] as libc::c_int) != 0 && + s[1] == ':' as u8) { + let rest = if s.len() == 2 { + ~"" + } else { + s.slice(2, s.len()).to_owned() + }; + return Some((s.slice(0,1).to_owned(), rest)); + } + None + } + } +} + +#[cfg(test)] +mod tests { + use option::{None, Some}; + use path::{PosixPath, WindowsPath, windows}; + use str; + + #[test] + fn test_double_slash_collapsing() { + let path = PosixPath("tmp/"); + let path = path.push("/hmm"); + let path = path.normalize(); + assert_eq!(~"tmp/hmm", path.to_str()); + + let path = WindowsPath("tmp/"); + let path = path.push("/hmm"); + let path = path.normalize(); + assert_eq!(~"tmp\\hmm", path.to_str()); + } + + #[test] + fn test_filetype_foo_bar() { + let wp = PosixPath("foo.bar"); + assert_eq!(wp.filetype(), Some(~".bar")); + + let wp = WindowsPath("foo.bar"); + assert_eq!(wp.filetype(), Some(~".bar")); + } + + #[test] + fn test_filetype_foo() { + let wp = PosixPath("foo"); + assert_eq!(wp.filetype(), None); + + let wp = WindowsPath("foo"); + assert_eq!(wp.filetype(), None); + } + + #[test] + fn test_posix_paths() { + fn t(wp: &PosixPath, s: &str) { + let ss = wp.to_str(); + let sss = str::to_owned(s); + if (ss != sss) { + debug!("got %s", ss); + debug!("expected %s", sss); + assert_eq!(ss, sss); + } + } + + t(&(PosixPath("hi")), "hi"); + t(&(PosixPath("/lib")), "/lib"); + t(&(PosixPath("hi/there")), "hi/there"); + t(&(PosixPath("hi/there.txt")), "hi/there.txt"); + + t(&(PosixPath("hi/there.txt")), "hi/there.txt"); + t(&(PosixPath("hi/there.txt") + .with_filetype("")), "hi/there"); + + t(&(PosixPath("/a/b/c/there.txt") + .with_dirname("hi")), "hi/there.txt"); + + t(&(PosixPath("hi/there.txt") + .with_dirname(".")), "./there.txt"); + + t(&(PosixPath("a/b/c") + .push("..")), "a/b/c/.."); + + t(&(PosixPath("there.txt") + .with_filetype("o")), "there.o"); + + t(&(PosixPath("hi/there.txt") + .with_filetype("o")), "hi/there.o"); + + t(&(PosixPath("hi/there.txt") + .with_filetype("o") + .with_dirname("/usr/lib")), + "/usr/lib/there.o"); + + t(&(PosixPath("hi/there.txt") + .with_filetype("o") + .with_dirname("/usr/lib/")), + "/usr/lib/there.o"); + + t(&(PosixPath("hi/there.txt") + .with_filetype("o") + .with_dirname("/usr//lib//")), + "/usr/lib/there.o"); + + t(&(PosixPath("/usr/bin/rust") + .push_many([~"lib", ~"thingy.so"]) + .with_filestem("librustc")), + "/usr/bin/rust/lib/librustc.so"); + + } + + #[test] + fn test_normalize() { + fn t(wp: &PosixPath, s: &str) { + let ss = wp.to_str(); + let sss = str::to_owned(s); + if (ss != sss) { + debug!("got %s", ss); + debug!("expected %s", sss); + assert_eq!(ss, sss); + } + } + + t(&(PosixPath("hi/there.txt") + .with_dirname(".").normalize()), "there.txt"); + + t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()), + "a/foo.txt"); + + t(&(PosixPath("a/b/c") + .push("..").normalize()), "a/b"); + } + + #[test] + fn test_extract_unc_prefixes() { + assert!(windows::extract_unc_prefix("\\\\").is_none()); + assert!(windows::extract_unc_prefix("//").is_none()); + assert!(windows::extract_unc_prefix("\\\\hi").is_none()); + assert!(windows::extract_unc_prefix("//hi").is_none()); + assert!(windows::extract_unc_prefix("\\\\hi\\") == + Some((~"hi", ~"\\"))); + assert!(windows::extract_unc_prefix("//hi\\") == + Some((~"hi", ~"\\"))); + assert!(windows::extract_unc_prefix("\\\\hi\\there") == + Some((~"hi", ~"\\there"))); + assert!(windows::extract_unc_prefix("//hi/there") == + Some((~"hi", ~"/there"))); + assert!(windows::extract_unc_prefix( + "\\\\hi\\there\\friends.txt") == + Some((~"hi", ~"\\there\\friends.txt"))); + assert!(windows::extract_unc_prefix( + "//hi\\there\\friends.txt") == + Some((~"hi", ~"\\there\\friends.txt"))); + } + + #[test] + fn test_extract_drive_prefixes() { + assert!(windows::extract_drive_prefix("c").is_none()); + assert!(windows::extract_drive_prefix("c:") == + Some((~"c", ~""))); + assert!(windows::extract_drive_prefix("d:") == + Some((~"d", ~""))); + assert!(windows::extract_drive_prefix("z:") == + Some((~"z", ~""))); + assert!(windows::extract_drive_prefix("c:\\hi") == + Some((~"c", ~"\\hi"))); + assert!(windows::extract_drive_prefix("d:hi") == + Some((~"d", ~"hi"))); + assert!(windows::extract_drive_prefix("c:hi\\there.txt") == + Some((~"c", ~"hi\\there.txt"))); + assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") == + Some((~"c", ~"\\hi\\there.txt"))); + } + + #[test] + fn test_windows_paths() { + fn t(wp: &WindowsPath, s: &str) { + let ss = wp.to_str(); + let sss = str::to_owned(s); + if (ss != sss) { + debug!("got %s", ss); + debug!("expected %s", sss); + assert_eq!(ss, sss); + } + } + + t(&(WindowsPath("hi")), "hi"); + t(&(WindowsPath("hi/there")), "hi\\there"); + t(&(WindowsPath("hi/there.txt")), "hi\\there.txt"); + + t(&(WindowsPath("there.txt") + .with_filetype("o")), "there.o"); + + t(&(WindowsPath("hi/there.txt") + .with_filetype("o")), "hi\\there.o"); + + t(&(WindowsPath("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files A")), + "c:\\program files A\\there.o"); + + t(&(WindowsPath("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files B\\")), + "c:\\program files B\\there.o"); + + t(&(WindowsPath("hi/there.txt") + .with_filetype("o") + .with_dirname("c:\\program files C\\/")), + "c:\\program files C\\there.o"); + + t(&(WindowsPath("c:\\program files (x86)\\rust") + .push_many([~"lib", ~"thingy.dll"]) + .with_filename("librustc.dll")), + "c:\\program files (x86)\\rust\\lib\\librustc.dll"); + + t(&(WindowsPath("\\\\computer\\share") + .unsafe_join(&WindowsPath("\\a"))), + "\\\\computer\\a"); + + t(&(WindowsPath("//computer/share") + .unsafe_join(&WindowsPath("\\a"))), + "\\\\computer\\a"); + + t(&(WindowsPath("//computer/share") + .unsafe_join(&WindowsPath("\\\\computer\\share"))), + "\\\\computer\\share"); + + t(&(WindowsPath("C:/whatever") + .unsafe_join(&WindowsPath("//computer/share/a/b"))), + "\\\\computer\\share\\a\\b"); + + t(&(WindowsPath("C:") + .unsafe_join(&WindowsPath("D:/foo"))), + "D:\\foo"); + + t(&(WindowsPath("C:") + .unsafe_join(&WindowsPath("B"))), + "C:B"); + + t(&(WindowsPath("C:") + .unsafe_join(&WindowsPath("/foo"))), + "C:\\foo"); + + t(&(WindowsPath("C:\\") + .unsafe_join(&WindowsPath("\\bar"))), + "C:\\bar"); + + t(&(WindowsPath("") + .unsafe_join(&WindowsPath(""))), + ""); + + t(&(WindowsPath("") + .unsafe_join(&WindowsPath("a"))), + "a"); + + t(&(WindowsPath("") + .unsafe_join(&WindowsPath("C:\\a"))), + "C:\\a"); + + t(&(WindowsPath("c:\\foo") + .normalize()), + "C:\\foo"); + } + + #[test] + fn test_windows_path_restrictions() { + assert_eq!(WindowsPath("hi").is_restricted(), false); + assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true); + assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true); + assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true); + } +} diff --git a/src/libstd/pipes.rs b/src/libstd/pipes.rs new file mode 100644 index 00000000000..1c2b57bb2df --- /dev/null +++ b/src/libstd/pipes.rs @@ -0,0 +1,916 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! Runtime support for message passing with protocol enforcement. + + +Pipes consist of two endpoints. One endpoint can send messages and +the other can receive messages. The set of legal messages and which +directions they can flow at any given point are determined by a +protocol. Below is an example protocol. + +~~~ +proto! pingpong ( + ping: send { + ping -> pong + } + pong: recv { + pong -> ping + } +) +~~~ + +The `proto!` syntax extension will convert this into a module called +`pingpong`, which includes a set of types and functions that can be +used to write programs that follow the pingpong protocol. + +*/ + +/* IMPLEMENTATION NOTES + +The initial design for this feature is available at: + +https://github.com/eholk/rust/wiki/Proposal-for-channel-contracts + +Much of the design in that document is still accurate. There are +several components for the pipe implementation. First of all is the +syntax extension. To see how that works, it is best see comments in +libsyntax/ext/pipes.rs. + +This module includes two related pieces of the runtime +implementation: support for unbounded and bounded +protocols. The main difference between the two is the type of the +buffer that is carried along in the endpoint data structures. + + +The heart of the implementation is the packet type. It contains a +header and a payload field. Much of the code in this module deals with +the header field. This is where the synchronization information is +stored. In the case of a bounded protocol, the header also includes a +pointer to the buffer the packet is contained in. + +Packets represent a single message in a protocol. The payload field +gets instatiated at the type of the message, which is usually an enum +generated by the pipe compiler. Packets are conceptually single use, +although in bounded protocols they are reused each time around the +loop. + + +Packets are usually handled through a send_packet_buffered or +recv_packet_buffered object. Each packet is referenced by one +send_packet and one recv_packet, and these wrappers enforce that only +one end can send and only one end can receive. The structs also +include a destructor that marks packets are terminated if the sender +or receiver destroys the object before sending or receiving a value. + +The *_packet_buffered structs take two type parameters. The first is +the message type for the current packet (or state). The second +represents the type of the whole buffer. For bounded protocols, the +protocol compiler generates a struct with a field for each protocol +state. This generated struct is used as the buffer type parameter. For +unbounded protocols, the buffer is simply one packet, so there is a +shorthand struct called send_packet and recv_packet, where the buffer +type is just `packet<T>`. Using the same underlying structure for both +bounded and unbounded protocols allows for less code duplication. + +*/ + +use container::Container; +use cast::{forget, transmute, transmute_copy}; +use either::{Either, Left, Right}; +use kinds::Owned; +use libc; +use ops::Drop; +use option::{None, Option, Some}; +use unstable::finally::Finally; +use unstable::intrinsics; +use ptr; +use ptr::Ptr; +use task; +use vec; +use vec::OwnedVector; +use util::replace; + +static SPIN_COUNT: uint = 0; + +macro_rules! move_it ( + { $x:expr } => ( unsafe { let y = *ptr::to_unsafe_ptr(&($x)); y } ) +) + +#[deriving(Eq)] +enum State { + Empty, + Full, + Blocked, + Terminated +} + +pub struct BufferHeader { + // Tracks whether this buffer needs to be freed. We can probably + // get away with restricting it to 0 or 1, if we're careful. + ref_count: int, + + // We may want a drop, and to be careful about stringing this + // thing along. +} + +pub fn BufferHeader() -> BufferHeader { + BufferHeader { + ref_count: 0 + } +} + +// This is for protocols to associate extra data to thread around. +pub struct Buffer<T> { + header: BufferHeader, + data: T, +} + +pub struct PacketHeader { + state: State, + blocked_task: *rust_task, + + // This is a transmute_copy of a ~buffer, that can also be cast + // to a buffer_header if need be. + buffer: *libc::c_void, +} + +pub fn PacketHeader() -> PacketHeader { + PacketHeader { + state: Empty, + blocked_task: ptr::null(), + buffer: ptr::null() + } +} + +pub impl PacketHeader { + // Returns the old state. + unsafe fn mark_blocked(&mut self, this: *rust_task) -> State { + rustrt::rust_task_ref(this); + let old_task = swap_task(&mut self.blocked_task, this); + assert!(old_task.is_null()); + swap_state_acq(&mut self.state, Blocked) + } + + unsafe fn unblock(&mut self) { + let old_task = swap_task(&mut self.blocked_task, ptr::null()); + if !old_task.is_null() { + rustrt::rust_task_deref(old_task) + } + match swap_state_acq(&mut self.state, Empty) { + Empty | Blocked => (), + Terminated => self.state = Terminated, + Full => self.state = Full + } + } + + // unsafe because this can do weird things to the space/time + // continuum. It ends making multiple unique pointers to the same + // thing. You'll probably want to forget them when you're done. + unsafe fn buf_header(&mut self) -> ~BufferHeader { + assert!(self.buffer.is_not_null()); + transmute_copy(&self.buffer) + } + + fn set_buffer<T:Owned>(&mut self, b: ~Buffer<T>) { + unsafe { + self.buffer = transmute_copy(&b); + } + } +} + +pub struct Packet<T> { + header: PacketHeader, + payload: Option<T>, +} + +pub trait HasBuffer { + fn set_buffer(&mut self, b: *libc::c_void); +} + +impl<T:Owned> HasBuffer for Packet<T> { + fn set_buffer(&mut self, b: *libc::c_void) { + self.header.buffer = b; + } +} + +pub fn mk_packet<T:Owned>() -> Packet<T> { + Packet { + header: PacketHeader(), + payload: None, + } +} +fn unibuffer<T>() -> ~Buffer<Packet<T>> { + let mut b = ~Buffer { + header: BufferHeader(), + data: Packet { + header: PacketHeader(), + payload: None, + } + }; + + unsafe { + b.data.header.buffer = transmute_copy(&b); + } + b +} + +pub fn packet<T>() -> *mut Packet<T> { + let mut b = unibuffer(); + let p = ptr::to_mut_unsafe_ptr(&mut b.data); + // We'll take over memory management from here. + unsafe { + forget(b); + } + p +} + +pub fn entangle_buffer<T:Owned,Tstart:Owned>( + mut buffer: ~Buffer<T>, + init: &fn(*libc::c_void, x: &mut T) -> *mut Packet<Tstart>) + -> (SendPacketBuffered<Tstart, T>, RecvPacketBuffered<Tstart, T>) { + unsafe { + let p = init(transmute_copy(&buffer), &mut buffer.data); + forget(buffer); + (SendPacketBuffered(p), RecvPacketBuffered(p)) + } +} + +pub fn swap_task(dst: &mut *rust_task, src: *rust_task) -> *rust_task { + // It might be worth making both acquire and release versions of + // this. + unsafe { + transmute(intrinsics::atomic_xchg(transmute(dst), src as int)) + } +} + +#[allow(non_camel_case_types)] +pub type rust_task = libc::c_void; + +pub mod rustrt { + use libc; + use super::rust_task; + + pub extern { + #[rust_stack] + unsafe fn rust_get_task() -> *rust_task; + #[rust_stack] + unsafe fn rust_task_ref(task: *rust_task); + unsafe fn rust_task_deref(task: *rust_task); + + #[rust_stack] + unsafe fn task_clear_event_reject(task: *rust_task); + + unsafe fn task_wait_event(this: *rust_task, + killed: &mut *libc::c_void) + -> bool; + unsafe fn task_signal_event(target: *rust_task, event: *libc::c_void); + } +} + +fn wait_event(this: *rust_task) -> *libc::c_void { + unsafe { + let mut event = ptr::null(); + + let killed = rustrt::task_wait_event(this, &mut event); + if killed && !task::failing() { + fail!("killed") + } + event + } +} + +fn swap_state_acq(dst: &mut State, src: State) -> State { + unsafe { + transmute(intrinsics::atomic_xchg_acq(transmute(dst), src as int)) + } +} + +fn swap_state_rel(dst: &mut State, src: State) -> State { + unsafe { + transmute(intrinsics::atomic_xchg_rel(transmute(dst), src as int)) + } +} + +pub unsafe fn get_buffer<T>(p: *mut PacketHeader) -> ~Buffer<T> { + transmute((*p).buf_header()) +} + +// This could probably be done with SharedMutableState to avoid move_it!(). +struct BufferResource<T> { + buffer: ~Buffer<T>, + +} + +#[unsafe_destructor] +impl<T> Drop for BufferResource<T> { + fn finalize(&self) { + unsafe { + let this: &mut BufferResource<T> = transmute(self); + + let mut b = move_it!(this.buffer); + //let p = ptr::to_unsafe_ptr(*b); + //error!("drop %?", p); + let old_count = intrinsics::atomic_xsub_rel( + &mut b.header.ref_count, + 1); + //let old_count = atomic_xchng_rel(b.header.ref_count, 0); + if old_count == 1 { + // The new count is 0. + + // go go gadget drop glue + } + else { + forget(b) + } + } + } +} + +fn BufferResource<T>(mut b: ~Buffer<T>) -> BufferResource<T> { + //let p = ptr::to_unsafe_ptr(*b); + //error!("take %?", p); + unsafe { + intrinsics::atomic_xadd_acq(&mut b.header.ref_count, 1); + } + + BufferResource { + // tjc: ???? + buffer: b + } +} + +pub fn send<T,Tbuffer>(mut p: SendPacketBuffered<T,Tbuffer>, + payload: T) + -> bool { + let header = p.header(); + let p_ = p.unwrap(); + let p = unsafe { &mut *p_ }; + assert_eq!(ptr::to_unsafe_ptr(&(p.header)), header); + assert!(p.payload.is_none()); + p.payload = Some(payload); + let old_state = swap_state_rel(&mut p.header.state, Full); + match old_state { + Empty => { + // Yay, fastpath. + + // The receiver will eventually clean this up. + //unsafe { forget(p); } + return true; + } + Full => fail!("duplicate send"), + Blocked => { + debug!("waking up task for %?", p_); + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + unsafe { + rustrt::task_signal_event( + old_task, + ptr::to_unsafe_ptr(&(p.header)) as *libc::c_void); + rustrt::rust_task_deref(old_task); + } + } + + // The receiver will eventually clean this up. + //unsafe { forget(p); } + return true; + } + Terminated => { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + return false; + } + } +} + +/** Receives a message from a pipe. + +Fails if the sender closes the connection. + +*/ +pub fn recv<T:Owned,Tbuffer:Owned>( + p: RecvPacketBuffered<T, Tbuffer>) -> T { + try_recv(p).expect("connection closed") +} + +/** Attempts to receive a message from a pipe. + +Returns `None` if the sender has closed the connection without sending +a message, or `Some(T)` if a message was received. + +*/ +pub fn try_recv<T:Owned,Tbuffer:Owned>(mut p: RecvPacketBuffered<T, Tbuffer>) + -> Option<T> { + let p_ = p.unwrap(); + let p = unsafe { &mut *p_ }; + + do (|| { + try_recv_(p) + }).finally { + unsafe { + if task::failing() { + p.header.state = Terminated; + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + rustrt::rust_task_deref(old_task); + } + } + } + } +} + +fn try_recv_<T:Owned>(p: &mut Packet<T>) -> Option<T> { + // optimistic path + match p.header.state { + Full => { + let payload = replace(&mut p.payload, None); + p.header.state = Empty; + return Some(payload.unwrap()) + }, + Terminated => return None, + _ => {} + } + + // regular path + let this = unsafe { rustrt::rust_get_task() }; + unsafe { + rustrt::task_clear_event_reject(this); + rustrt::rust_task_ref(this); + }; + debug!("blocked = %x this = %x", p.header.blocked_task as uint, + this as uint); + let old_task = swap_task(&mut p.header.blocked_task, this); + debug!("blocked = %x this = %x old_task = %x", + p.header.blocked_task as uint, + this as uint, old_task as uint); + assert!(old_task.is_null()); + let mut first = true; + let mut count = SPIN_COUNT; + loop { + unsafe { + rustrt::task_clear_event_reject(this); + } + + let old_state = swap_state_acq(&mut p.header.state, + Blocked); + match old_state { + Empty => { + debug!("no data available on %?, going to sleep.", p); + if count == 0 { + wait_event(this); + } + else { + count -= 1; + // FIXME (#524): Putting the yield here destroys a lot + // of the benefit of spinning, since we still go into + // the scheduler at every iteration. However, without + // this everything spins too much because we end up + // sometimes blocking the thing we are waiting on. + task::yield(); + } + debug!("woke up, p.state = %?", copy p.header.state); + } + Blocked => if first { + fail!("blocking on already blocked packet") + }, + Full => { + let payload = replace(&mut p.payload, None); + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + unsafe { + rustrt::rust_task_deref(old_task); + } + } + p.header.state = Empty; + return Some(payload.unwrap()) + } + Terminated => { + // This assert detects when we've accidentally unsafely + // casted too big of a number to a state. + assert_eq!(old_state, Terminated); + + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + unsafe { + rustrt::rust_task_deref(old_task); + } + } + return None; + } + } + first = false; + } +} + +/// Returns true if messages are available. +pub fn peek<T:Owned,Tb:Owned>(p: &mut RecvPacketBuffered<T, Tb>) -> bool { + unsafe { + match (*p.header()).state { + Empty | Terminated => false, + Blocked => fail!("peeking on blocked packet"), + Full => true + } + } +} + +fn sender_terminate<T:Owned>(p: *mut Packet<T>) { + let p = unsafe { + &mut *p + }; + match swap_state_rel(&mut p.header.state, Terminated) { + Empty => { + // The receiver will eventually clean up. + } + Blocked => { + // wake up the target + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + unsafe { + rustrt::task_signal_event( + old_task, + ptr::to_unsafe_ptr(&(p.header)) as *libc::c_void); + rustrt::rust_task_deref(old_task); + } + } + // The receiver will eventually clean up. + } + Full => { + // This is impossible + fail!("you dun goofed") + } + Terminated => { + assert!(p.header.blocked_task.is_null()); + // I have to clean up, use drop_glue + } + } +} + +fn receiver_terminate<T:Owned>(p: *mut Packet<T>) { + let p = unsafe { + &mut *p + }; + match swap_state_rel(&mut p.header.state, Terminated) { + Empty => { + assert!(p.header.blocked_task.is_null()); + // the sender will clean up + } + Blocked => { + let old_task = swap_task(&mut p.header.blocked_task, ptr::null()); + if !old_task.is_null() { + unsafe { + rustrt::rust_task_deref(old_task); + assert_eq!(old_task, rustrt::rust_get_task()); + } + } + } + Terminated | Full => { + assert!(p.header.blocked_task.is_null()); + // I have to clean up, use drop_glue + } + } +} + +/** Returns when one of the packet headers reports data is available. + +This function is primarily intended for building higher level waiting +functions, such as `select`, `select2`, etc. + +It takes a vector slice of packet_headers and returns an index into +that vector. The index points to an endpoint that has either been +closed by the sender or has a message waiting to be received. + +*/ +pub fn wait_many<T: Selectable>(pkts: &mut [T]) -> uint { + let this = unsafe { + rustrt::rust_get_task() + }; + + unsafe { + rustrt::task_clear_event_reject(this); + } + + let mut data_avail = false; + let mut ready_packet = pkts.len(); + for vec::eachi_mut(pkts) |i, p| { + unsafe { + let p = &mut *p.header(); + let old = p.mark_blocked(this); + match old { + Full | Terminated => { + data_avail = true; + ready_packet = i; + (*p).state = old; + break; + } + Blocked => fail!("blocking on blocked packet"), + Empty => () + } + } + } + + while !data_avail { + debug!("sleeping on %? packets", pkts.len()); + let event = wait_event(this) as *PacketHeader; + + let mut pos = None; + for vec::eachi_mut(pkts) |i, p| { + if p.header() == event { + pos = Some(i); + break; + } + }; + + match pos { + Some(i) => { + ready_packet = i; + data_avail = true; + } + None => debug!("ignoring spurious event, %?", event) + } + } + + debug!("%?", &mut pkts[ready_packet]); + + for vec::each_mut(pkts) |p| { + unsafe { + (*p.header()).unblock() + } + } + + debug!("%?, %?", ready_packet, &mut pkts[ready_packet]); + + unsafe { + assert!((*pkts[ready_packet].header()).state == Full + || (*pkts[ready_packet].header()).state == Terminated); + } + + ready_packet +} + +/** The sending end of a pipe. It can be used to send exactly one +message. + +*/ +pub type SendPacket<T> = SendPacketBuffered<T, Packet<T>>; + +pub fn SendPacket<T>(p: *mut Packet<T>) -> SendPacket<T> { + SendPacketBuffered(p) +} + +pub struct SendPacketBuffered<T, Tbuffer> { + p: Option<*mut Packet<T>>, + buffer: Option<BufferResource<Tbuffer>>, +} + +#[unsafe_destructor] +impl<T:Owned,Tbuffer:Owned> Drop for SendPacketBuffered<T,Tbuffer> { + fn finalize(&self) { + unsafe { + let this: &mut SendPacketBuffered<T,Tbuffer> = transmute(self); + if this.p != None { + let p = replace(&mut this.p, None); + sender_terminate(p.unwrap()) + } + } + } +} + +pub fn SendPacketBuffered<T,Tbuffer>(p: *mut Packet<T>) + -> SendPacketBuffered<T,Tbuffer> { + SendPacketBuffered { + p: Some(p), + buffer: unsafe { + Some(BufferResource(get_buffer(&mut (*p).header))) + } + } +} + +pub impl<T,Tbuffer> SendPacketBuffered<T,Tbuffer> { + fn unwrap(&mut self) -> *mut Packet<T> { + replace(&mut self.p, None).unwrap() + } + + fn header(&mut self) -> *mut PacketHeader { + match self.p { + Some(packet) => unsafe { + let packet = &mut *packet; + let header = ptr::to_mut_unsafe_ptr(&mut packet.header); + header + }, + None => fail!("packet already consumed") + } + } + + fn reuse_buffer(&mut self) -> BufferResource<Tbuffer> { + //error!("send reuse_buffer"); + replace(&mut self.buffer, None).unwrap() + } +} + +/// Represents the receive end of a pipe. It can receive exactly one +/// message. +pub type RecvPacket<T> = RecvPacketBuffered<T, Packet<T>>; + +pub fn RecvPacket<T>(p: *mut Packet<T>) -> RecvPacket<T> { + RecvPacketBuffered(p) +} + +pub struct RecvPacketBuffered<T, Tbuffer> { + p: Option<*mut Packet<T>>, + buffer: Option<BufferResource<Tbuffer>>, +} + +#[unsafe_destructor] +impl<T:Owned,Tbuffer:Owned> Drop for RecvPacketBuffered<T,Tbuffer> { + fn finalize(&self) { + unsafe { + let this: &mut RecvPacketBuffered<T,Tbuffer> = transmute(self); + if this.p != None { + let p = replace(&mut this.p, None); + receiver_terminate(p.unwrap()) + } + } + } +} + +pub impl<T:Owned,Tbuffer:Owned> RecvPacketBuffered<T, Tbuffer> { + fn unwrap(&mut self) -> *mut Packet<T> { + replace(&mut self.p, None).unwrap() + } + + fn reuse_buffer(&mut self) -> BufferResource<Tbuffer> { + replace(&mut self.buffer, None).unwrap() + } +} + +impl<T:Owned,Tbuffer:Owned> Selectable for RecvPacketBuffered<T, Tbuffer> { + fn header(&mut self) -> *mut PacketHeader { + match self.p { + Some(packet) => unsafe { + let packet = &mut *packet; + let header = ptr::to_mut_unsafe_ptr(&mut packet.header); + header + }, + None => fail!("packet already consumed") + } + } +} + +pub fn RecvPacketBuffered<T,Tbuffer>(p: *mut Packet<T>) + -> RecvPacketBuffered<T,Tbuffer> { + RecvPacketBuffered { + p: Some(p), + buffer: unsafe { + Some(BufferResource(get_buffer(&mut (*p).header))) + } + } +} + +pub fn entangle<T>() -> (SendPacket<T>, RecvPacket<T>) { + let p = packet(); + (SendPacket(p), RecvPacket(p)) +} + +/** Receives a message from one of two endpoints. + +The return value is `left` if the first endpoint received something, +or `right` if the second endpoint receives something. In each case, +the result includes the other endpoint as well so it can be used +again. Below is an example of using `select2`. + +~~~ +match select2(a, b) { + left((none, b)) { + // endpoint a was closed. + } + right((a, none)) { + // endpoint b was closed. + } + left((Some(_), b)) { + // endpoint a received a message + } + right(a, Some(_)) { + // endpoint b received a message. + } +} +~~~ + +Sometimes messages will be available on both endpoints at once. In +this case, `select2` may return either `left` or `right`. + +*/ +pub fn select2<A:Owned,Ab:Owned,B:Owned,Bb:Owned>( + mut a: RecvPacketBuffered<A, Ab>, + mut b: RecvPacketBuffered<B, Bb>) + -> Either<(Option<A>, RecvPacketBuffered<B, Bb>), + (RecvPacketBuffered<A, Ab>, Option<B>)> { + let mut endpoints = [ a.header(), b.header() ]; + let i = wait_many(endpoints); + match i { + 0 => Left((try_recv(a), b)), + 1 => Right((a, try_recv(b))), + _ => fail!("select2 return an invalid packet") + } +} + +pub trait Selectable { + fn header(&mut self) -> *mut PacketHeader; +} + +impl Selectable for *mut PacketHeader { + fn header(&mut self) -> *mut PacketHeader { *self } +} + +/// Returns the index of an endpoint that is ready to receive. +pub fn selecti<T:Selectable>(endpoints: &mut [T]) -> uint { + wait_many(endpoints) +} + +/// Returns 0 or 1 depending on which endpoint is ready to receive +pub fn select2i<A:Selectable,B:Selectable>(a: &mut A, b: &mut B) + -> Either<(), ()> { + let mut endpoints = [ a.header(), b.header() ]; + match wait_many(endpoints) { + 0 => Left(()), + 1 => Right(()), + _ => fail!("wait returned unexpected index") + } +} + +/// Waits on a set of endpoints. Returns a message, its index, and a +/// list of the remaining endpoints. +pub fn select<T:Owned,Tb:Owned>(mut endpoints: ~[RecvPacketBuffered<T, Tb>]) + -> (uint, + Option<T>, + ~[RecvPacketBuffered<T, Tb>]) { + let mut endpoint_headers = ~[]; + for vec::each_mut(endpoints) |endpoint| { + endpoint_headers.push(endpoint.header()); + } + + let ready = wait_many(endpoint_headers); + let mut remaining = endpoints; + let port = remaining.swap_remove(ready); + let result = try_recv(port); + (ready, result, remaining) +} + +pub mod rt { + use option::{None, Option, Some}; + + // These are used to hide the option constructors from the + // compiler because their names are changing + pub fn make_some<T>(val: T) -> Option<T> { Some(val) } + pub fn make_none<T>() -> Option<T> { None } +} + +#[cfg(test)] +mod test { + use either::Right; + use comm::{Chan, Port, oneshot, recv_one, stream, Select2, + GenericChan, Peekable}; + + #[test] + fn test_select2() { + let (p1, c1) = stream(); + let (p2, c2) = stream(); + + c1.send(~"abc"); + + let mut tuple = (p1, p2); + match tuple.select() { + Right(_) => fail!(), + _ => (), + } + + c2.send(123); + } + + #[test] + fn test_oneshot() { + let (p, c) = oneshot(); + + c.send(()); + + recv_one(p) + } + + #[test] + fn test_peek_terminated() { + let (port, chan): (Port<int>, Chan<int>) = stream(); + + { + // Destroy the channel + let _chan = chan; + } + + assert!(!port.peek()); + } +} diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs new file mode 100644 index 00000000000..58d0c40efa0 --- /dev/null +++ b/src/libstd/prelude.rs @@ -0,0 +1,111 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! The Rust prelude. Imported into every module by default. + +/* Reexported core operators */ + +pub use either::{Either, Left, Right}; +pub use kinds::{Const, Copy, Owned}; +pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not}; +pub use ops::{BitAnd, BitOr, BitXor}; +pub use ops::{Drop}; +pub use ops::{Shl, Shr, Index}; +pub use option::{Option, Some, None}; +pub use result::{Result, Ok, Err}; + +/* Reexported functions */ + +pub use io::{print, println}; + +/* Reexported types and traits */ + +pub use clone::{Clone, DeepClone}; +pub use cmp::{Eq, ApproxEq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv}; +pub use char::Char; +pub use container::{Container, Mutable, Map, Set}; +pub use hash::Hash; +pub use old_iter::{BaseIter, ReverseIter, MutableIter, ExtendedIter, EqIter}; +pub use old_iter::{CopyableIter, CopyableOrderedIter, CopyableNonstrictIter}; +pub use old_iter::{ExtendedMutableIter}; +pub use iter::Times; +pub use num::{Num, NumCast}; +pub use num::{Orderable, Signed, Unsigned, Round}; +pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic}; +pub use num::{Integer, Fractional, Real, RealExt}; +pub use num::{Bitwise, BitCount, Bounded}; +pub use num::{Primitive, Int, Float}; +pub use path::GenericPath; +pub use path::Path; +pub use path::PosixPath; +pub use path::WindowsPath; +pub use ptr::Ptr; +pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr}; +pub use str::{StrSlice, OwnedStr}; +pub use from_str::{FromStr}; +pub use to_bytes::IterBytes; +pub use to_str::{ToStr, ToStrConsume}; +pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps}; +pub use tuple::{CloneableTuple2, CloneableTuple3, CloneableTuple4, CloneableTuple5}; +pub use tuple::{CloneableTuple6, CloneableTuple7, CloneableTuple8, CloneableTuple9}; +pub use tuple::{CloneableTuple10, CloneableTuple11, CloneableTuple12}; +pub use tuple::{ImmutableTuple2, ImmutableTuple3, ImmutableTuple4, ImmutableTuple5}; +pub use tuple::{ImmutableTuple6, ImmutableTuple7, ImmutableTuple8, ImmutableTuple9}; +pub use tuple::{ImmutableTuple10, ImmutableTuple11, ImmutableTuple12}; +pub use vec::{CopyableVector, ImmutableVector}; +pub use vec::{ImmutableEqVector, ImmutableCopyableVector}; +pub use vec::{OwnedVector, OwnedCopyableVector, MutableVector}; +pub use io::{Reader, ReaderUtil, Writer, WriterUtil}; + +/* Reexported runtime types */ +pub use comm::{stream, Port, Chan, GenericChan, GenericSmartChan, GenericPort, Peekable}; +pub use task::spawn; + +/* Reexported modules */ + +pub use at_vec; +pub use bool; +pub use cast; +pub use char; +pub use cmp; +pub use either; +pub use f32; +pub use f64; +pub use float; +pub use i16; +pub use i32; +pub use i64; +pub use i8; +pub use int; +pub use io; +pub use iter; +pub use old_iter; +pub use libc; +pub use local_data; +pub use num; +pub use ops; +pub use option; +pub use os; +pub use path; +pub use comm; +pub use unstable; +pub use ptr; +pub use rand; +pub use result; +pub use str; +pub use sys; +pub use task; +pub use to_str; +pub use u16; +pub use u32; +pub use u64; +pub use u8; +pub use uint; +pub use vec; diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs deleted file mode 100644 index f1e0027146c..00000000000 --- a/src/libstd/priority_queue.rs +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! A priority queue implemented with a binary heap - -use core::old_iter::BaseIter; -use core::unstable::intrinsics::{move_val_init, init}; -use core::unstable::intrinsics::uninit; -use core::util::{replace, swap}; - -pub struct PriorityQueue<T> { - priv data: ~[T], -} - -impl<T:Ord> BaseIter<T> for PriorityQueue<T> { - /// Visit all values in the underlying vector. - /// - /// The values are **not** visited in order. - fn each(&self, f: &fn(&T) -> bool) -> bool { self.data.each(f) } - - fn size_hint(&self) -> Option<uint> { self.data.size_hint() } -} - -impl<T:Ord> Container for PriorityQueue<T> { - /// Returns the length of the queue - fn len(&const self) -> uint { vec::uniq_len(&const self.data) } - - /// Returns true if a queue contains no elements - fn is_empty(&const self) -> bool { self.len() == 0 } -} - -impl<T:Ord> Mutable for PriorityQueue<T> { - /// Drop all items from the queue - fn clear(&mut self) { self.data.truncate(0) } -} - -pub impl <T:Ord> PriorityQueue<T> { - /// Returns the greatest item in the queue - fails if empty - fn top<'a>(&'a self) -> &'a T { &self.data[0] } - - /// Returns the greatest item in the queue - None if empty - fn maybe_top<'a>(&'a self) -> Option<&'a T> { - if self.is_empty() { None } else { Some(self.top()) } - } - - /// Returns the number of elements the queue can hold without reallocating - fn capacity(&self) -> uint { vec::capacity(&self.data) } - - fn reserve(&mut self, n: uint) { vec::reserve(&mut self.data, n) } - - fn reserve_at_least(&mut self, n: uint) { - vec::reserve_at_least(&mut self.data, n) - } - - /// Pop the greatest item from the queue - fails if empty - fn pop(&mut self) -> T { - let mut item = self.data.pop(); - if !self.is_empty() { - swap(&mut item, &mut self.data[0]); - self.siftdown(0); - } - item - } - - /// Pop the greatest item from the queue - None if empty - fn maybe_pop(&mut self) -> Option<T> { - if self.is_empty() { None } else { Some(self.pop()) } - } - - /// Push an item onto the queue - fn push(&mut self, item: T) { - self.data.push(item); - let new_len = self.len() - 1; - self.siftup(0, new_len); - } - - /// Optimized version of a push followed by a pop - fn push_pop(&mut self, mut item: T) -> T { - if !self.is_empty() && self.data[0] > item { - swap(&mut item, &mut self.data[0]); - self.siftdown(0); - } - item - } - - /// Optimized version of a pop followed by a push - fails if empty - fn replace(&mut self, mut item: T) -> T { - swap(&mut item, &mut self.data[0]); - self.siftdown(0); - item - } - - /// Consume the PriorityQueue and return the underlying vector - fn to_vec(self) -> ~[T] { let PriorityQueue{data: v} = self; v } - - /// Consume the PriorityQueue and return a vector in sorted - /// (ascending) order - fn to_sorted_vec(self) -> ~[T] { - let mut q = self; - let mut end = q.len(); - while end > 1 { - end -= 1; - vec::swap(q.data, 0, end); - q.siftdown_range(0, end) - } - q.to_vec() - } - - /// Create an empty PriorityQueue - fn new() -> PriorityQueue<T> { PriorityQueue{data: ~[],} } - - /// Create a PriorityQueue from a vector (heapify) - fn from_vec(xs: ~[T]) -> PriorityQueue<T> { - let mut q = PriorityQueue{data: xs,}; - let mut n = q.len() / 2; - while n > 0 { - n -= 1; - q.siftdown(n) - } - q - } - - // The implementations of siftup and siftdown use unsafe blocks in - // order to move an element out of the vector (leaving behind a - // zeroed element), shift along the others and move it back into the - // vector over the junk element. This reduces the constant factor - // compared to using swaps, which involves twice as many moves. - - priv fn siftup(&mut self, start: uint, mut pos: uint) { - unsafe { - let new = replace(&mut self.data[pos], init()); - - while pos > start { - let parent = (pos - 1) >> 1; - if new > self.data[parent] { - let x = replace(&mut self.data[parent], init()); - move_val_init(&mut self.data[pos], x); - pos = parent; - loop - } - break - } - move_val_init(&mut self.data[pos], new); - } - } - - priv fn siftdown_range(&mut self, mut pos: uint, end: uint) { - unsafe { - let start = pos; - let new = replace(&mut self.data[pos], init()); - - let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - if right < end && !(self.data[child] > self.data[right]) { - child = right; - } - let x = replace(&mut self.data[child], init()); - move_val_init(&mut self.data[pos], x); - pos = child; - child = 2 * pos + 1; - } - - move_val_init(&mut self.data[pos], new); - self.siftup(start, pos); - } - } - - priv fn siftdown(&mut self, pos: uint) { - let len = self.len(); - self.siftdown_range(pos, len); - } -} - -#[cfg(test)] -mod tests { - use sort::merge_sort; - use core::cmp::le; - use priority_queue::PriorityQueue::{from_vec, new}; - - #[test] - fn test_top_and_pop() { - let data = ~[2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; - let mut sorted = merge_sort(data, le); - let mut heap = from_vec(data); - while !heap.is_empty() { - assert_eq!(heap.top(), sorted.last()); - assert_eq!(heap.pop(), sorted.pop()); - } - } - - #[test] - fn test_push() { - let mut heap = from_vec(~[2, 4, 9]); - assert_eq!(heap.len(), 3); - assert!(*heap.top() == 9); - heap.push(11); - assert_eq!(heap.len(), 4); - assert!(*heap.top() == 11); - heap.push(5); - assert_eq!(heap.len(), 5); - assert!(*heap.top() == 11); - heap.push(27); - assert_eq!(heap.len(), 6); - assert!(*heap.top() == 27); - heap.push(3); - assert_eq!(heap.len(), 7); - assert!(*heap.top() == 27); - heap.push(103); - assert_eq!(heap.len(), 8); - assert!(*heap.top() == 103); - } - - #[test] - fn test_push_unique() { - let mut heap = from_vec(~[~2, ~4, ~9]); - assert_eq!(heap.len(), 3); - assert!(*heap.top() == ~9); - heap.push(~11); - assert_eq!(heap.len(), 4); - assert!(*heap.top() == ~11); - heap.push(~5); - assert_eq!(heap.len(), 5); - assert!(*heap.top() == ~11); - heap.push(~27); - assert_eq!(heap.len(), 6); - assert!(*heap.top() == ~27); - heap.push(~3); - assert_eq!(heap.len(), 7); - assert!(*heap.top() == ~27); - heap.push(~103); - assert_eq!(heap.len(), 8); - assert!(*heap.top() == ~103); - } - - #[test] - fn test_push_pop() { - let mut heap = from_vec(~[5, 5, 2, 1, 3]); - assert_eq!(heap.len(), 5); - assert_eq!(heap.push_pop(6), 6); - assert_eq!(heap.len(), 5); - assert_eq!(heap.push_pop(0), 5); - assert_eq!(heap.len(), 5); - assert_eq!(heap.push_pop(4), 5); - assert_eq!(heap.len(), 5); - assert_eq!(heap.push_pop(1), 4); - assert_eq!(heap.len(), 5); - } - - #[test] - fn test_replace() { - let mut heap = from_vec(~[5, 5, 2, 1, 3]); - assert_eq!(heap.len(), 5); - assert_eq!(heap.replace(6), 5); - assert_eq!(heap.len(), 5); - assert_eq!(heap.replace(0), 6); - assert_eq!(heap.len(), 5); - assert_eq!(heap.replace(4), 5); - assert_eq!(heap.len(), 5); - assert_eq!(heap.replace(1), 4); - assert_eq!(heap.len(), 5); - } - - fn check_to_vec(data: ~[int]) { - let heap = from_vec(copy data); - assert_eq!(merge_sort((copy heap).to_vec(), le), merge_sort(data, le)); - assert_eq!(heap.to_sorted_vec(), merge_sort(data, le)); - } - - #[test] - fn test_to_vec() { - check_to_vec(~[]); - check_to_vec(~[5]); - check_to_vec(~[3, 2]); - check_to_vec(~[2, 3]); - check_to_vec(~[5, 1, 2]); - check_to_vec(~[1, 100, 2, 3]); - check_to_vec(~[1, 3, 5, 7, 9, 2, 4, 6, 8, 0]); - check_to_vec(~[2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - check_to_vec(~[9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]); - check_to_vec(~[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - check_to_vec(~[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); - check_to_vec(~[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]); - check_to_vec(~[5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_empty_pop() { let mut heap = new::<int>(); heap.pop(); } - - #[test] - fn test_empty_maybe_pop() { - let mut heap = new::<int>(); - assert!(heap.maybe_pop().is_none()); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_empty_top() { let empty = new::<int>(); empty.top(); } - - #[test] - fn test_empty_maybe_top() { - let empty = new::<int>(); - assert!(empty.maybe_top().is_none()); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_empty_replace() { let mut heap = new(); heap.replace(5); } -} diff --git a/src/libstd/ptr.rs b/src/libstd/ptr.rs new file mode 100644 index 00000000000..f6cc00ccc86 --- /dev/null +++ b/src/libstd/ptr.rs @@ -0,0 +1,561 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Unsafe pointer utility functions + +use cast; +use libc; +use libc::{c_void, size_t}; +use option::{Option, Some, None}; +use sys; + +#[cfg(not(test))] use cmp::{Eq, Ord}; +use uint; + +pub mod libc_ { + use libc::c_void; + use libc; + + #[nolink] + #[abi = "cdecl"] + pub extern { + #[rust_stack] + unsafe fn memmove(dest: *mut c_void, + src: *const c_void, + n: libc::size_t) + -> *c_void; + + #[rust_stack] + unsafe fn memset(dest: *mut c_void, + c: libc::c_int, + len: libc::size_t) + -> *c_void; + } +} + +/// Calculate the offset from a pointer +#[inline(always)] +pub fn offset<T>(ptr: *T, count: uint) -> *T { + (ptr as uint + count * sys::size_of::<T>()) as *T +} + +/// Calculate the offset from a const pointer +#[inline(always)] +pub fn const_offset<T>(ptr: *const T, count: uint) -> *const T { + (ptr as uint + count * sys::size_of::<T>()) as *T +} + +/// Calculate the offset from a mut pointer +#[inline(always)] +pub fn mut_offset<T>(ptr: *mut T, count: uint) -> *mut T { + (ptr as uint + count * sys::size_of::<T>()) as *mut T +} + +/// Return the offset of the first null pointer in `buf`. +#[inline(always)] +pub unsafe fn buf_len<T>(buf: **T) -> uint { + position(buf, |i| *i == null()) +} + +/// Return the first offset `i` such that `f(buf[i]) == true`. +#[inline(always)] +pub unsafe fn position<T>(buf: *T, f: &fn(&T) -> bool) -> uint { + let mut i = 0; + loop { + if f(&(*offset(buf, i))) { return i; } + else { i += 1; } + } +} + +/// Create an unsafe null pointer +#[inline(always)] +pub fn null<T>() -> *T { unsafe { cast::transmute(0u) } } + +/// Create an unsafe mutable null pointer +#[inline(always)] +pub fn mut_null<T>() -> *mut T { unsafe { cast::transmute(0u) } } + +/// Returns true if the pointer is equal to the null pointer. +#[inline(always)] +pub fn is_null<T>(ptr: *const T) -> bool { ptr == null() } + +/// Returns true if the pointer is not equal to the null pointer. +#[inline(always)] +pub fn is_not_null<T>(ptr: *const T) -> bool { !is_null(ptr) } + +/** + * Copies data from one location to another + * + * Copies `count` elements (not bytes) from `src` to `dst`. The source + * and destination may overlap. + */ +#[inline(always)] +#[cfg(target_word_size = "32")] +pub unsafe fn copy_memory<T>(dst: *mut T, src: *const T, count: uint) { + use unstable::intrinsics::memmove32; + let n = count * sys::size_of::<T>(); + memmove32(dst as *mut u8, src as *u8, n as u32); +} +#[inline(always)] +#[cfg(target_word_size = "64")] +pub unsafe fn copy_memory<T>(dst: *mut T, src: *const T, count: uint) { + use unstable::intrinsics::memmove64; + let n = count * sys::size_of::<T>(); + memmove64(dst as *mut u8, src as *u8, n as u64); +} + +#[inline(always)] +pub unsafe fn set_memory<T>(dst: *mut T, c: int, count: uint) { + let n = count * sys::size_of::<T>(); + libc_::memset(dst as *mut c_void, c as libc::c_int, n as size_t); +} + +/** + Transform a region pointer - &T - to an unsafe pointer - *T. + This is safe, but is implemented with an unsafe block due to + transmute. +*/ +#[inline(always)] +pub fn to_unsafe_ptr<T>(thing: &T) -> *T { + unsafe { cast::transmute(thing) } +} + +/** + Transform a const region pointer - &const T - to a const unsafe pointer - + *const T. This is safe, but is implemented with an unsafe block due to + transmute. +*/ +#[inline(always)] +pub fn to_const_unsafe_ptr<T>(thing: &const T) -> *const T { + unsafe { cast::transmute(thing) } +} + +/** + Transform a mutable region pointer - &mut T - to a mutable unsafe pointer - + *mut T. This is safe, but is implemented with an unsafe block due to + transmute. +*/ +#[inline(always)] +pub fn to_mut_unsafe_ptr<T>(thing: &mut T) -> *mut T { + unsafe { cast::transmute(thing) } +} + +/** + Cast a region pointer - &T - to a uint. + This is safe, but is implemented with an unsafe block due to + transmute. + + (I couldn't think of a cutesy name for this one.) +*/ +#[inline(always)] +pub fn to_uint<T>(thing: &T) -> uint { + unsafe { + cast::transmute(thing) + } +} + +/// Determine if two borrowed pointers point to the same thing. +#[inline(always)] +pub fn ref_eq<'a,'b,T>(thing: &'a T, other: &'b T) -> bool { + to_uint(thing) == to_uint(other) +} + +/** + Given a **T (pointer to an array of pointers), + iterate through each *T, up to the provided `len`, + passing to the provided callback function + + SAFETY NOTE: Pointer-arithmetic. Dragons be here. +*/ +pub unsafe fn array_each_with_len<T>(arr: **T, len: uint, cb: &fn(*T)) { + debug!("array_each_with_len: before iterate"); + if (arr as uint == 0) { + fail!("ptr::array_each_with_len failure: arr input is null pointer"); + } + //let start_ptr = *arr; + uint::iterate(0, len, |e| { + let n = offset(arr, e); + cb(*n); + true + }); + debug!("array_each_with_len: after iterate"); +} + +/** + Given a null-pointer-terminated **T (pointer to + an array of pointers), iterate through each *T, + passing to the provided callback function + + SAFETY NOTE: This will only work with a null-terminated + pointer array. Barely less-dodgey Pointer Arithmetic. + Dragons be here. +*/ +pub unsafe fn array_each<T>(arr: **T, cb: &fn(*T)) { + if (arr as uint == 0) { + fail!("ptr::array_each_with_len failure: arr input is null pointer"); + } + let len = buf_len(arr); + debug!("array_each inferred len: %u", + len); + array_each_with_len(arr, len, cb); +} + +pub trait Ptr<T> { + fn is_null(&const self) -> bool; + fn is_not_null(&const self) -> bool; + unsafe fn to_option(&const self) -> Option<&T>; + fn offset(&self, count: uint) -> Self; +} + +/// Extension methods for immutable pointers +impl<T> Ptr<T> for *T { + /// Returns true if the pointer is equal to the null pointer. + #[inline(always)] + fn is_null(&const self) -> bool { is_null(*self) } + + /// Returns true if the pointer is not equal to the null pointer. + #[inline(always)] + fn is_not_null(&const self) -> bool { is_not_null(*self) } + + /// + /// Returns `None` if the pointer is null, or else returns the value wrapped + /// in `Some`. + /// + /// # Safety Notes + /// + /// While this method is useful for null-safety, it is important to note + /// that this is still an unsafe operation because the returned value could + /// be pointing to invalid memory. + /// + #[inline(always)] + unsafe fn to_option(&const self) -> Option<&T> { + if self.is_null() { None } else { + Some(cast::transmute(*self)) + } + } + + /// Calculates the offset from a pointer. + #[inline(always)] + fn offset(&self, count: uint) -> *T { offset(*self, count) } +} + +/// Extension methods for mutable pointers +impl<T> Ptr<T> for *mut T { + /// Returns true if the pointer is equal to the null pointer. + #[inline(always)] + fn is_null(&const self) -> bool { is_null(*self) } + + /// Returns true if the pointer is not equal to the null pointer. + #[inline(always)] + fn is_not_null(&const self) -> bool { is_not_null(*self) } + + /// + /// Returns `None` if the pointer is null, or else returns the value wrapped + /// in `Some`. + /// + /// # Safety Notes + /// + /// While this method is useful for null-safety, it is important to note + /// that this is still an unsafe operation because the returned value could + /// be pointing to invalid memory. + /// + #[inline(always)] + unsafe fn to_option(&const self) -> Option<&T> { + if self.is_null() { None } else { + Some(cast::transmute(*self)) + } + } + + /// Calculates the offset from a mutable pointer. + #[inline(always)] + fn offset(&self, count: uint) -> *mut T { mut_offset(*self, count) } +} + +// Equality for pointers +#[cfg(not(test))] +impl<T> Eq for *const T { + #[inline(always)] + fn eq(&self, other: &*const T) -> bool { + unsafe { + let a: uint = cast::transmute(*self); + let b: uint = cast::transmute(*other); + return a == b; + } + } + #[inline(always)] + fn ne(&self, other: &*const T) -> bool { !(*self).eq(other) } +} + +// Comparison for pointers +#[cfg(not(test))] +impl<T> Ord for *const T { + #[inline(always)] + fn lt(&self, other: &*const T) -> bool { + unsafe { + let a: uint = cast::transmute(*self); + let b: uint = cast::transmute(*other); + return a < b; + } + } + #[inline(always)] + fn le(&self, other: &*const T) -> bool { + unsafe { + let a: uint = cast::transmute(*self); + let b: uint = cast::transmute(*other); + return a <= b; + } + } + #[inline(always)] + fn ge(&self, other: &*const T) -> bool { + unsafe { + let a: uint = cast::transmute(*self); + let b: uint = cast::transmute(*other); + return a >= b; + } + } + #[inline(always)] + fn gt(&self, other: &*const T) -> bool { + unsafe { + let a: uint = cast::transmute(*self); + let b: uint = cast::transmute(*other); + return a > b; + } + } +} + +// Equality for region pointers +#[cfg(not(test))] +impl<'self,T:Eq> Eq for &'self T { + #[inline(always)] + fn eq(&self, other: & &'self T) -> bool { + return *(*self) == *(*other); + } + #[inline(always)] + fn ne(&self, other: & &'self T) -> bool { + return *(*self) != *(*other); + } +} + +// Comparison for region pointers +#[cfg(not(test))] +impl<'self,T:Ord> Ord for &'self T { + #[inline(always)] + fn lt(&self, other: & &'self T) -> bool { + *(*self) < *(*other) + } + #[inline(always)] + fn le(&self, other: & &'self T) -> bool { + *(*self) <= *(*other) + } + #[inline(always)] + fn ge(&self, other: & &'self T) -> bool { + *(*self) >= *(*other) + } + #[inline(always)] + fn gt(&self, other: & &'self T) -> bool { + *(*self) > *(*other) + } +} + +#[cfg(test)] +pub mod ptr_tests { + use super::*; + use prelude::*; + + #[test] + fn test() { + unsafe { + struct Pair { + fst: int, + snd: int + }; + let mut p = Pair {fst: 10, snd: 20}; + let pptr: *mut Pair = &mut p; + let iptr: *mut int = cast::transmute(pptr); + assert_eq!(*iptr, 10); + *iptr = 30; + assert_eq!(*iptr, 30); + assert_eq!(p.fst, 30); + + *pptr = Pair {fst: 50, snd: 60}; + assert_eq!(*iptr, 50); + assert_eq!(p.fst, 50); + assert_eq!(p.snd, 60); + + let v0 = ~[32000u16, 32001u16, 32002u16]; + let mut v1 = ~[0u16, 0u16, 0u16]; + + copy_memory(mut_offset(vec::raw::to_mut_ptr(v1), 1u), + offset(vec::raw::to_ptr(v0), 1u), 1u); + assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + copy_memory(vec::raw::to_mut_ptr(v1), + offset(vec::raw::to_ptr(v0), 2u), 1u); + assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && + v1[2] == 0u16)); + copy_memory(mut_offset(vec::raw::to_mut_ptr(v1), 2u), + vec::raw::to_ptr(v0), 1u); + assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && + v1[2] == 32000u16)); + } + } + + #[test] + fn test_position() { + use str::as_c_str; + use libc::c_char; + + let s = ~"hello"; + unsafe { + assert!(2u == as_c_str(s, |p| position(p, + |c| *c == 'l' as c_char))); + assert!(4u == as_c_str(s, |p| position(p, + |c| *c == 'o' as c_char))); + assert!(5u == as_c_str(s, |p| position(p, + |c| *c == 0 as c_char))); + } + } + + #[test] + fn test_buf_len() { + let s0 = ~"hello"; + let s1 = ~"there"; + let s2 = ~"thing"; + do str::as_c_str(s0) |p0| { + do str::as_c_str(s1) |p1| { + do str::as_c_str(s2) |p2| { + let v = ~[p0, p1, p2, null()]; + do vec::as_imm_buf(v) |vp, len| { + assert_eq!(unsafe { buf_len(vp) }, 3u); + assert_eq!(len, 4u); + } + } + } + } + } + + #[test] + fn test_is_null() { + let p: *int = null(); + assert!(p.is_null()); + assert!(!p.is_not_null()); + + let q = offset(p, 1u); + assert!(!q.is_null()); + assert!(q.is_not_null()); + + let mp: *mut int = mut_null(); + assert!(mp.is_null()); + assert!(!mp.is_not_null()); + + let mq = mp.offset(1u); + assert!(!mq.is_null()); + assert!(mq.is_not_null()); + } + + #[test] + fn test_to_option() { + let p: *int = null(); + // FIXME (#6641): Usage of unsafe methods in safe code doesn't cause an error. + assert_eq!(p.to_option(), None); + + let q: *int = &2; + assert_eq!(q.to_option().unwrap(), &2); // FIXME (#6641) + + let p: *mut int = mut_null(); + assert_eq!(p.to_option(), None); // FIXME (#6641) + + let q: *mut int = &mut 2; + assert_eq!(q.to_option().unwrap(), &2); // FIXME (#6641) + } + + #[test] + fn test_ptr_array_each_with_len() { + unsafe { + let one = ~"oneOne"; + let two = ~"twoTwo"; + let three = ~"threeThree"; + let arr: ~[*i8] = ~[ + ::cast::transmute(&one[0]), + ::cast::transmute(&two[0]), + ::cast::transmute(&three[0]), + ]; + let expected_arr = [ + one, two, three + ]; + let arr_ptr = &arr[0]; + let mut ctr = 0; + let mut iteration_count = 0; + array_each_with_len(arr_ptr, arr.len(), + |e| { + let actual = str::raw::from_c_str(e); + let expected = copy expected_arr[ctr]; + debug!( + "test_ptr_array_each e: %s, a: %s", + expected, actual); + assert_eq!(actual, expected); + ctr += 1; + iteration_count += 1; + }); + assert_eq!(iteration_count, 3u); + } + } + #[test] + fn test_ptr_array_each() { + unsafe { + let one = ~"oneOne"; + let two = ~"twoTwo"; + let three = ~"threeThree"; + let arr: ~[*i8] = ~[ + ::cast::transmute(&one[0]), + ::cast::transmute(&two[0]), + ::cast::transmute(&three[0]), + // fake a null terminator + 0 as *i8 + ]; + let expected_arr = [ + one, two, three + ]; + let arr_ptr = &arr[0]; + let mut ctr = 0; + let mut iteration_count = 0; + array_each(arr_ptr, |e| { + let actual = str::raw::from_c_str(e); + let expected = copy expected_arr[ctr]; + debug!( + "test_ptr_array_each e: %s, a: %s", + expected, actual); + assert_eq!(actual, expected); + ctr += 1; + iteration_count += 1; + }); + assert_eq!(iteration_count, 3); + } + } + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_ptr_array_each_with_len_null_ptr() { + unsafe { + array_each_with_len(0 as **libc::c_char, 1, |e| { + str::raw::from_c_str(e); + }); + } + } + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_ptr_array_each_null_ptr() { + unsafe { + array_each(0 as **libc::c_char, |e| { + str::raw::from_c_str(e); + }); + } + } +} diff --git a/src/libstd/rand.rs b/src/libstd/rand.rs new file mode 100644 index 00000000000..84dd7d187e4 --- /dev/null +++ b/src/libstd/rand.rs @@ -0,0 +1,1092 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +Random number generation. + +The key functions are `random()` and `RngUtil::gen()`. These are polymorphic +and so can be used to generate any type that implements `Rand`. Type inference +means that often a simple call to `rand::random()` or `rng.gen()` will +suffice, but sometimes an annotation is required, e.g. `rand::random::<float>()`. + +See the `distributions` submodule for sampling random numbers from +distributions like normal and exponential. + +# Examples +~~~ +use core::rand::RngUtil; + +fn main() { + let rng = rand::rng(); + if rng.gen() { // bool + println(fmt!("int: %d, uint: %u", rng.gen(), rng.gen())) + } +} +~~~ + +~~~ +fn main () { + let tuple_ptr = rand::random::<~(f64, char)>(); + println(fmt!("%?", tuple_ptr)) +} +~~~ +*/ + + +use int; +use prelude::*; +use str; +use u32; +use uint; +use util; +use vec; +use libc::size_t; + +#[path="rand/distributions.rs"] +pub mod distributions; + +/// A type that can be randomly generated using an Rng +pub trait Rand { + fn rand<R: Rng>(rng: &mut R) -> Self; +} + +impl Rand for int { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> int { + if int::bits == 32 { + rng.next() as int + } else { + rng.gen::<i64>() as int + } + } +} + +impl Rand for i8 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> i8 { + rng.next() as i8 + } +} + +impl Rand for i16 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> i16 { + rng.next() as i16 + } +} + +impl Rand for i32 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> i32 { + rng.next() as i32 + } +} + +impl Rand for i64 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> i64 { + (rng.next() as i64 << 32) | rng.next() as i64 + } +} + +impl Rand for uint { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> uint { + if uint::bits == 32 { + rng.next() as uint + } else { + rng.gen::<u64>() as uint + } + } +} + +impl Rand for u8 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> u8 { + rng.next() as u8 + } +} + +impl Rand for u16 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> u16 { + rng.next() as u16 + } +} + +impl Rand for u32 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> u32 { + rng.next() + } +} + +impl Rand for u64 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> u64 { + (rng.next() as u64 << 32) | rng.next() as u64 + } +} + +impl Rand for float { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> float { + rng.gen::<f64>() as float + } +} + +impl Rand for f32 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> f32 { + rng.gen::<f64>() as f32 + } +} + +static scale : f64 = (u32::max_value as f64) + 1.0f64; +impl Rand for f64 { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> f64 { + let u1 = rng.next() as f64; + let u2 = rng.next() as f64; + let u3 = rng.next() as f64; + + ((u1 / scale + u2) / scale + u3) / scale + } +} + +impl Rand for char { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> char { + rng.next() as char + } +} + +impl Rand for bool { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> bool { + rng.next() & 1u32 == 1u32 + } +} + +macro_rules! tuple_impl { + // use variables to indicate the arity of the tuple + ($($tyvar:ident),* ) => { + // the trailing commas are for the 1 tuple + impl< + $( $tyvar : Rand ),* + > Rand for ( $( $tyvar ),* , ) { + + #[inline] + fn rand<R: Rng>(_rng: &mut R) -> ( $( $tyvar ),* , ) { + ( + // use the $tyvar's to get the appropriate number of + // repeats (they're not actually needed) + $( + _rng.gen::<$tyvar>() + ),* + , + ) + } + } + } +} + +impl Rand for () { + #[inline] + fn rand<R: Rng>(_: &mut R) -> () { () } +} +tuple_impl!{A} +tuple_impl!{A, B} +tuple_impl!{A, B, C} +tuple_impl!{A, B, C, D} +tuple_impl!{A, B, C, D, E} +tuple_impl!{A, B, C, D, E, F} +tuple_impl!{A, B, C, D, E, F, G} +tuple_impl!{A, B, C, D, E, F, G, H} +tuple_impl!{A, B, C, D, E, F, G, H, I} +tuple_impl!{A, B, C, D, E, F, G, H, I, J} + +impl<T:Rand> Rand for Option<T> { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> Option<T> { + if rng.gen() { + Some(rng.gen()) + } else { + None + } + } +} + +impl<T: Rand> Rand for ~T { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> ~T { ~rng.gen() } +} + +impl<T: Rand> Rand for @T { + #[inline] + fn rand<R: Rng>(rng: &mut R) -> @T { @rng.gen() } +} + +#[abi = "cdecl"] +pub mod rustrt { + use libc::size_t; + + pub extern { + unsafe fn rand_seed_size() -> size_t; + unsafe fn rand_gen_seed(buf: *mut u8, sz: size_t); + } +} + +/// A random number generator +pub trait Rng { + /// Return the next random integer + pub fn next(&mut self) -> u32; +} + +/// A value with a particular weight compared to other values +pub struct Weighted<T> { + weight: uint, + item: T, +} + +pub trait RngUtil { + /// Return a random value of a Rand type + fn gen<T:Rand>(&mut self) -> T; + /** + * Return a int randomly chosen from the range [start, end), + * failing if start >= end + */ + fn gen_int_range(&mut self, start: int, end: int) -> int; + /** + * Return a uint randomly chosen from the range [start, end), + * failing if start >= end + */ + fn gen_uint_range(&mut self, start: uint, end: uint) -> uint; + /** + * Return a char randomly chosen from chars, failing if chars is empty + */ + fn gen_char_from(&mut self, chars: &str) -> char; + /** + * Return a bool with a 1 in n chance of true + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * println(fmt!("%b",rng.gen_weighted_bool(3))); + * } + * ~~~ + */ + fn gen_weighted_bool(&mut self, n: uint) -> bool; + /** + * Return a random string of the specified length composed of A-Z,a-z,0-9 + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * println(rng.gen_str(8)); + * } + * ~~~ + */ + fn gen_str(&mut self, len: uint) -> ~str; + /** + * Return a random byte string of the specified length + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * println(fmt!("%?",rng.gen_bytes(8))); + * } + * ~~~ + */ + fn gen_bytes(&mut self, len: uint) -> ~[u8]; + /** + * Choose an item randomly, failing if values is empty + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * println(fmt!("%d",rng.choose([1,2,4,8,16,32]))); + * } + * ~~~ + */ + fn choose<T:Copy>(&mut self, values: &[T]) -> T; + /// Choose Some(item) randomly, returning None if values is empty + fn choose_option<T:Copy>(&mut self, values: &[T]) -> Option<T>; + /** + * Choose an item respecting the relative weights, failing if the sum of + * the weights is 0 + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * let x = [rand::Weighted {weight: 4, item: 'a'}, + * rand::Weighted {weight: 2, item: 'b'}, + * rand::Weighted {weight: 2, item: 'c'}]; + * println(fmt!("%c",rng.choose_weighted(x))); + * } + * ~~~ + */ + fn choose_weighted<T:Copy>(&mut self, v : &[Weighted<T>]) -> T; + /** + * Choose Some(item) respecting the relative weights, returning none if + * the sum of the weights is 0 + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * let x = [rand::Weighted {weight: 4, item: 'a'}, + * rand::Weighted {weight: 2, item: 'b'}, + * rand::Weighted {weight: 2, item: 'c'}]; + * println(fmt!("%?",rng.choose_weighted_option(x))); + * } + * ~~~ + */ + fn choose_weighted_option<T:Copy>(&mut self, v: &[Weighted<T>]) + -> Option<T>; + /** + * Return a vec containing copies of the items, in order, where + * the weight of the item determines how many copies there are + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * let x = [rand::Weighted {weight: 4, item: 'a'}, + * rand::Weighted {weight: 2, item: 'b'}, + * rand::Weighted {weight: 2, item: 'c'}]; + * println(fmt!("%?",rng.weighted_vec(x))); + * } + * ~~~ + */ + fn weighted_vec<T:Copy>(&mut self, v: &[Weighted<T>]) -> ~[T]; + /** + * Shuffle a vec + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * println(fmt!("%?",rng.shuffle([1,2,3]))); + * } + * ~~~ + */ + fn shuffle<T:Copy>(&mut self, values: &[T]) -> ~[T]; + /** + * Shuffle a mutable vec in place + * + * *Example* + * + * ~~~ + * + * use core::rand::RngUtil; + * + * fn main() { + * rng = rand::rng(); + * let mut y = [1,2,3]; + * rng.shuffle_mut(y); + * println(fmt!("%?",y)); + * rng.shuffle_mut(y); + * println(fmt!("%?",y)); + * } + * ~~~ + */ + fn shuffle_mut<T>(&mut self, values: &mut [T]); +} + +/// Extension methods for random number generators +impl<R: Rng> RngUtil for R { + /// Return a random value for a Rand type + #[inline(always)] + fn gen<T: Rand>(&mut self) -> T { + Rand::rand(self) + } + + /** + * Return an int randomly chosen from the range [start, end), + * failing if start >= end + */ + fn gen_int_range(&mut self, start: int, end: int) -> int { + assert!(start < end); + start + int::abs(self.gen::<int>() % (end - start)) + } + + /** + * Return a uint randomly chosen from the range [start, end), + * failing if start >= end + */ + fn gen_uint_range(&mut self, start: uint, end: uint) -> uint { + assert!(start < end); + start + (self.gen::<uint>() % (end - start)) + } + + /** + * Return a char randomly chosen from chars, failing if chars is empty + */ + fn gen_char_from(&mut self, chars: &str) -> char { + assert!(!chars.is_empty()); + let mut cs = ~[]; + for str::each_char(chars) |c| { cs.push(c) } + self.choose(cs) + } + + /// Return a bool with a 1-in-n chance of true + fn gen_weighted_bool(&mut self, n: uint) -> bool { + if n == 0u { + true + } else { + self.gen_uint_range(1u, n + 1u) == 1u + } + } + + /** + * Return a random string of the specified length composed of A-Z,a-z,0-9 + */ + fn gen_str(&mut self, len: uint) -> ~str { + let charset = ~"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; + let mut s = ~""; + let mut i = 0u; + while (i < len) { + s = s + str::from_char(self.gen_char_from(charset)); + i += 1u; + } + s + } + + /// Return a random byte string of the specified length + fn gen_bytes(&mut self, len: uint) -> ~[u8] { + do vec::from_fn(len) |_i| { + self.gen() + } + } + + /// Choose an item randomly, failing if values is empty + fn choose<T:Copy>(&mut self, values: &[T]) -> T { + self.choose_option(values).get() + } + + /// Choose Some(item) randomly, returning None if values is empty + fn choose_option<T:Copy>(&mut self, values: &[T]) -> Option<T> { + if values.is_empty() { + None + } else { + Some(values[self.gen_uint_range(0u, values.len())]) + } + } + /** + * Choose an item respecting the relative weights, failing if the sum of + * the weights is 0 + */ + fn choose_weighted<T:Copy>(&mut self, v: &[Weighted<T>]) -> T { + self.choose_weighted_option(v).get() + } + + /** + * Choose Some(item) respecting the relative weights, returning none if + * the sum of the weights is 0 + */ + fn choose_weighted_option<T:Copy>(&mut self, v: &[Weighted<T>]) + -> Option<T> { + let mut total = 0u; + for v.each |item| { + total += item.weight; + } + if total == 0u { + return None; + } + let chosen = self.gen_uint_range(0u, total); + let mut so_far = 0u; + for v.each |item| { + so_far += item.weight; + if so_far > chosen { + return Some(item.item); + } + } + util::unreachable(); + } + + /** + * Return a vec containing copies of the items, in order, where + * the weight of the item determines how many copies there are + */ + fn weighted_vec<T:Copy>(&mut self, v: &[Weighted<T>]) -> ~[T] { + let mut r = ~[]; + for v.each |item| { + for uint::range(0u, item.weight) |_i| { + r.push(item.item); + } + } + r + } + + /// Shuffle a vec + fn shuffle<T:Copy>(&mut self, values: &[T]) -> ~[T] { + let mut m = vec::to_owned(values); + self.shuffle_mut(m); + m + } + + /// Shuffle a mutable vec in place + fn shuffle_mut<T>(&mut self, values: &mut [T]) { + let mut i = values.len(); + while i >= 2u { + // invariant: elements with index >= i have been locked in place. + i -= 1u; + // lock element i in place. + vec::swap(values, i, self.gen_uint_range(0u, i + 1u)); + } + } +} + +/// Create a random number generator with a default algorithm and seed. +pub fn rng() -> IsaacRng { + IsaacRng::new() +} + +static RAND_SIZE_LEN: u32 = 8; +static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; + +/// A random number generator that uses the [ISAAC +/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29). +pub struct IsaacRng { + priv cnt: u32, + priv rsl: [u32, .. RAND_SIZE], + priv mem: [u32, .. RAND_SIZE], + priv a: u32, + priv b: u32, + priv c: u32 +} + +pub impl IsaacRng { + /// Create an ISAAC random number generator with a random seed. + fn new() -> IsaacRng { + IsaacRng::new_seeded(seed()) + } + + /// Create an ISAAC random number generator with a seed. This can be any + /// length, although the maximum number of bytes used is 1024 and any more + /// will be silently ignored. A generator constructed with a given seed + /// will generate the same sequence of values as all other generators + /// constructed with the same seed. + fn new_seeded(seed: &[u8]) -> IsaacRng { + let mut rng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 + }; + + let array_size = sys::size_of_val(&rng.rsl); + let copy_length = cmp::min(array_size, seed.len()); + + // manually create a &mut [u8] slice of randrsl to copy into. + let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; + vec::bytes::copy_memory(dest, seed, copy_length); + rng.init(true); + rng + } + + /// Create an ISAAC random number generator using the default + /// fixed seed. + fn new_unseeded() -> IsaacRng { + let mut rng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 + }; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + priv fn init(&mut self, use_rsl: bool) { + macro_rules! init_mut_many ( + ($( $var:ident ),* = $val:expr ) => { + let mut $( $var = $val ),*; + } + ); + init_mut_many!(a, b, c, d, e, f, g, h = 0x9e3779b9); + + + macro_rules! mix( + () => {{ + a^=b<<11; d+=a; b+=c; + b^=c>>2; e+=b; c+=d; + c^=d<<8; f+=c; d+=e; + d^=e>>16; g+=d; e+=f; + e^=f<<10; h+=e; f+=g; + f^=g>>4; a+=f; g+=h; + g^=h<<8; b+=g; h+=a; + h^=a>>9; c+=h; a+=b; + }} + ); + + for 4.times { mix!(); } + + if use_rsl { + macro_rules! memloop ( + ($arr:expr) => {{ + for u32::range_step(0, RAND_SIZE, 8) |i| { + a+=$arr[i ]; b+=$arr[i+1]; + c+=$arr[i+2]; d+=$arr[i+3]; + e+=$arr[i+4]; f+=$arr[i+5]; + g+=$arr[i+6]; h+=$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + ); + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for u32::range_step(0, RAND_SIZE, 8) |i| { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac(); + } + + /// Refills the output buffer (`self.rsl`) + #[inline] + priv fn isaac(&mut self) { + self.c += 1; + // abbreviations + let mut a = self.a, b = self.b + self.c; + + static midpoint: uint = RAND_SIZE as uint / 2; + + macro_rules! ind (($x:expr) => { + self.mem[($x >> 2) & (RAND_SIZE - 1)] + }); + macro_rules! rngstep( + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = if $shift < 0 { + a >> -$shift as uint + } else { + a << $shift as uint + }; + + let x = self.mem[base + mr_offset]; + a = (a ^ mix) + self.mem[base + m2_offset]; + let y = ind!(x) + a + b; + self.mem[base + mr_offset] = y; + + b = ind!(y >> RAND_SIZE_LEN) + x; + self.rsl[base + mr_offset] = b; + }} + ); + + for [(0, midpoint), (midpoint, 0)].each |&(mr_offset, m2_offset)| { + for uint::range_step(0, midpoint, 4) |base| { + rngstep!(0, 13); + rngstep!(1, -6); + rngstep!(2, 2); + rngstep!(3, -16); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE; + } +} + +impl Rng for IsaacRng { + #[inline(always)] + fn next(&mut self) -> u32 { + if self.cnt == 0 { + // make some more numbers + self.isaac(); + } + self.cnt -= 1; + self.rsl[self.cnt] + } +} + +/// An [Xorshift random number +/// generator](http://en.wikipedia.org/wiki/Xorshift). Not suitable for +/// cryptographic purposes. +pub struct XorShiftRng { + priv x: u32, + priv y: u32, + priv z: u32, + priv w: u32, +} + +impl Rng for XorShiftRng { + #[inline] + pub fn next(&mut self) -> u32 { + let x = self.x; + let t = x ^ (x << 11); + self.x = self.y; + self.y = self.z; + self.z = self.w; + let w = self.w; + self.w = w ^ (w >> 19) ^ (t ^ (t >> 8)); + self.w + } +} + +pub impl XorShiftRng { + /// Create an xor shift random number generator with a default seed. + fn new() -> XorShiftRng { + // constants taken from http://en.wikipedia.org/wiki/Xorshift + XorShiftRng::new_seeded(123456789u32, + 362436069u32, + 521288629u32, + 88675123u32) + } + + /** + * Create a random number generator using the specified seed. A generator + * constructed with a given seed will generate the same sequence of values as + * all other generators constructed with the same seed. + */ + fn new_seeded(x: u32, y: u32, z: u32, w: u32) -> XorShiftRng { + XorShiftRng { + x: x, + y: y, + z: z, + w: w, + } + } +} + +/// Create a new random seed. +pub fn seed() -> ~[u8] { + unsafe { + let n = rustrt::rand_seed_size() as uint; + let mut s = vec::from_elem(n, 0_u8); + do vec::as_mut_buf(s) |p, sz| { + rustrt::rand_gen_seed(p, sz as size_t) + } + s + } +} + +// used to make space in TLS for a random number generator +fn tls_rng_state(_v: @@mut IsaacRng) {} + +/** + * Gives back a lazily initialized task-local random number generator, + * seeded by the system. Intended to be used in method chaining style, ie + * `task_rng().gen::<int>()`. + */ +#[inline] +pub fn task_rng() -> @@mut IsaacRng { + let r : Option<@@mut IsaacRng>; + unsafe { + r = local_data::local_data_get(tls_rng_state); + } + match r { + None => { + unsafe { + let rng = @@mut IsaacRng::new_seeded(seed()); + local_data::local_data_set(tls_rng_state, rng); + rng + } + } + Some(rng) => rng + } +} + +// Allow direct chaining with `task_rng` +impl<R: Rng> Rng for @@mut R { + #[inline(always)] + fn next(&mut self) -> u32 { + match *self { + @@ref mut r => r.next() + } + } +} + +/** + * Returns a random value of a Rand type, using the task's random number + * generator. + */ +#[inline] +pub fn random<T: Rand>() -> T { + match *task_rng() { + @ref mut r => r.gen() + } +} + +#[cfg(test)] +mod tests { + use option::{Option, Some}; + use super::*; + + #[test] + fn test_rng_seeded() { + let seed = seed(); + let mut ra = IsaacRng::new_seeded(seed); + let mut rb = IsaacRng::new_seeded(seed); + assert_eq!(ra.gen_str(100u), rb.gen_str(100u)); + } + + #[test] + fn test_rng_seeded_custom_seed() { + // much shorter than generated seeds which are 1024 bytes + let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; + let mut ra = IsaacRng::new_seeded(seed); + let mut rb = IsaacRng::new_seeded(seed); + assert_eq!(ra.gen_str(100u), rb.gen_str(100u)); + } + + #[test] + fn test_rng_seeded_custom_seed2() { + let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; + let mut ra = IsaacRng::new_seeded(seed); + // Regression test that isaac is actually using the above vector + let r = ra.next(); + error!("%?", r); + assert!(r == 890007737u32 // on x86_64 + || r == 2935188040u32); // on x86 + } + + #[test] + fn test_gen_int_range() { + let mut r = rng(); + let a = r.gen_int_range(-3, 42); + assert!(a >= -3 && a < 42); + assert_eq!(r.gen_int_range(0, 1), 0); + assert_eq!(r.gen_int_range(-12, -11), -12); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_gen_int_from_fail() { + let mut r = rng(); + r.gen_int_range(5, -2); + } + + #[test] + fn test_gen_uint_range() { + let mut r = rng(); + let a = r.gen_uint_range(3u, 42u); + assert!(a >= 3u && a < 42u); + assert_eq!(r.gen_uint_range(0u, 1u), 0u); + assert_eq!(r.gen_uint_range(12u, 13u), 12u); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_gen_uint_range_fail() { + let mut r = rng(); + r.gen_uint_range(5u, 2u); + } + + #[test] + fn test_gen_float() { + let mut r = rng(); + let a = r.gen::<float>(); + let b = r.gen::<float>(); + debug!((a, b)); + } + + #[test] + fn test_gen_weighted_bool() { + let mut r = rng(); + assert_eq!(r.gen_weighted_bool(0u), true); + assert_eq!(r.gen_weighted_bool(1u), true); + } + + #[test] + fn test_gen_str() { + let mut r = rng(); + debug!(r.gen_str(10u)); + debug!(r.gen_str(10u)); + debug!(r.gen_str(10u)); + assert_eq!(r.gen_str(0u).len(), 0u); + assert_eq!(r.gen_str(10u).len(), 10u); + assert_eq!(r.gen_str(16u).len(), 16u); + } + + #[test] + fn test_gen_bytes() { + let mut r = rng(); + assert_eq!(r.gen_bytes(0u).len(), 0u); + assert_eq!(r.gen_bytes(10u).len(), 10u); + assert_eq!(r.gen_bytes(16u).len(), 16u); + } + + #[test] + fn test_choose() { + let mut r = rng(); + assert_eq!(r.choose([1, 1, 1]), 1); + } + + #[test] + fn test_choose_option() { + let mut r = rng(); + let x: Option<int> = r.choose_option([]); + assert!(x.is_none()); + assert_eq!(r.choose_option([1, 1, 1]), Some(1)); + } + + #[test] + fn test_choose_weighted() { + let mut r = rng(); + assert!(r.choose_weighted(~[ + Weighted { weight: 1u, item: 42 }, + ]) == 42); + assert!(r.choose_weighted(~[ + Weighted { weight: 0u, item: 42 }, + Weighted { weight: 1u, item: 43 }, + ]) == 43); + } + + #[test] + fn test_choose_weighted_option() { + let mut r = rng(); + assert!(r.choose_weighted_option(~[ + Weighted { weight: 1u, item: 42 }, + ]) == Some(42)); + assert!(r.choose_weighted_option(~[ + Weighted { weight: 0u, item: 42 }, + Weighted { weight: 1u, item: 43 }, + ]) == Some(43)); + let v: Option<int> = r.choose_weighted_option([]); + assert!(v.is_none()); + } + + #[test] + fn test_weighted_vec() { + let mut r = rng(); + let empty: ~[int] = ~[]; + assert_eq!(r.weighted_vec(~[]), empty); + assert!(r.weighted_vec(~[ + Weighted { weight: 0u, item: 3u }, + Weighted { weight: 1u, item: 2u }, + Weighted { weight: 2u, item: 1u }, + ]) == ~[2u, 1u, 1u]); + } + + #[test] + fn test_shuffle() { + let mut r = rng(); + let empty: ~[int] = ~[]; + assert_eq!(r.shuffle(~[]), empty); + assert_eq!(r.shuffle(~[1, 1, 1]), ~[1, 1, 1]); + } + + #[test] + fn test_task_rng() { + let mut r = task_rng(); + r.gen::<int>(); + assert_eq!(r.shuffle(~[1, 1, 1]), ~[1, 1, 1]); + assert_eq!(r.gen_uint_range(0u, 1u), 0u); + } + + #[test] + fn test_random() { + // not sure how to test this aside from just getting some values + let _n : uint = random(); + let _f : f32 = random(); + let _o : Option<Option<i8>> = random(); + let _many : ((), + (~uint, @int, ~Option<~(@char, ~(@bool,))>), + (u8, i8, u16, i16, u32, i32, u64, i64), + (f32, (f64, (float,)))) = random(); + } + + #[test] + fn compare_isaac_implementation() { + // This is to verify that the implementation of the ISAAC rng is + // correct (i.e. matches the output of the upstream implementation, + // which is in the runtime) + use vec; + use libc::size_t; + + #[abi = "cdecl"] + mod rustrt { + use libc::size_t; + + #[allow(non_camel_case_types)] // runtime type + pub enum rust_rng {} + + pub extern { + unsafe fn rand_new_seeded(buf: *u8, sz: size_t) -> *rust_rng; + unsafe fn rand_next(rng: *rust_rng) -> u32; + unsafe fn rand_free(rng: *rust_rng); + } + } + + // run against several seeds + for 10.times { + unsafe { + let seed = super::seed(); + let rt_rng = do vec::as_imm_buf(seed) |p, sz| { + rustrt::rand_new_seeded(p, sz as size_t) + }; + let mut rng = IsaacRng::new_seeded(seed); + + for 10000.times { + assert_eq!(rng.next(), rustrt::rand_next(rt_rng)); + } + rustrt::rand_free(rt_rng); + } + } + } +} diff --git a/src/libstd/rand/distributions.rs b/src/libstd/rand/distributions.rs new file mode 100644 index 00000000000..72cff5111e7 --- /dev/null +++ b/src/libstd/rand/distributions.rs @@ -0,0 +1,148 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Sampling from random distributions + +// Some implementations use the Ziggurat method +// https://en.wikipedia.org/wiki/Ziggurat_algorithm +// +// The version used here is ZIGNOR [Doornik 2005, "An Improved +// Ziggurat Method to Generate Normal Random Samples"] which is slower +// (about double, it generates an extra random number) than the +// canonical version [Marsaglia & Tsang 2000, "The Ziggurat Method for +// Generating Random Variables"], but more robust. If one wanted, one +// could implement VIZIGNOR the ZIGNOR paper for more speed. + +use prelude::*; +use rand::{Rng,Rand}; + +mod ziggurat_tables; + +// inlining should mean there is no performance penalty for this +#[inline(always)] +fn ziggurat<R:Rng>(rng: &mut R, + center_u: bool, + X: ziggurat_tables::ZigTable, + F: ziggurat_tables::ZigTable, + F_DIFF: ziggurat_tables::ZigTable, + pdf: &'static fn(f64) -> f64, // probability density function + zero_case: &'static fn(&mut R, f64) -> f64) -> f64 { + loop { + let u = if center_u {2.0 * rng.gen() - 1.0} else {rng.gen()}; + let i: uint = rng.gen::<uint>() & 0xff; + let x = u * X[i]; + + let test_x = if center_u {f64::abs(x)} else {x}; + + // algebraically equivalent to |u| < X[i+1]/X[i] (or u < X[i+1]/X[i]) + if test_x < X[i + 1] { + return x; + } + if i == 0 { + return zero_case(rng, u); + } + // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 + if F[i+1] + F_DIFF[i+1] * rng.gen() < pdf(x) { + return x; + } + } +} + +/// A wrapper around an `f64` to generate N(0, 1) random numbers (a.k.a. a +/// standard normal, or Gaussian). Multiplying the generated values by the +/// desired standard deviation `sigma` then adding the desired mean `mu` will +/// give N(mu, sigma^2) distributed random numbers. +/// +/// Note that this has to be unwrapped before use as an `f64` (using either +/// `*` or `cast::transmute` is safe). +/// +/// # Example +/// +/// ~~~ +/// use core::rand::distributions::StandardNormal; +/// +/// fn main() { +/// let normal = 2.0 + (*rand::random::<StandardNormal>()) * 3.0; +/// println(fmt!("%f is from a N(2, 9) distribution", normal)) +/// } +/// ~~~ +pub struct StandardNormal(f64); + +impl Rand for StandardNormal { + fn rand<R:Rng>(rng: &mut R) -> StandardNormal { + #[inline(always)] + fn pdf(x: f64) -> f64 { + f64::exp((-x*x/2.0) as f64) as f64 + } + #[inline(always)] + fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 { + // compute a random number in the tail by hand + + // strange initial conditions, because the loop is not + // do-while, so the condition should be true on the first + // run, they get overwritten anyway (0 < 1, so these are + // good). + let mut x = 1.0, y = 0.0; + + // XXX infinities? + while -2.0*y < x * x { + x = f64::ln(rng.gen()) / ziggurat_tables::ZIG_NORM_R; + y = f64::ln(rng.gen()); + } + if u < 0.0 {x-ziggurat_tables::ZIG_NORM_R} else {ziggurat_tables::ZIG_NORM_R-x} + } + + StandardNormal(ziggurat( + rng, + true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, &ziggurat_tables::ZIG_NORM_F_DIFF, + pdf, zero_case)) + } +} + +/// A wrapper around an `f64` to generate Exp(1) random numbers. Dividing by +/// the desired rate `lambda` will give Exp(lambda) distributed random +/// numbers. +/// +/// Note that this has to be unwrapped before use as an `f64` (using either +/// `*` or `cast::transmute` is safe). +/// +/// # Example +/// +/// ~~~ +/// use core::rand::distributions::Exp1; +/// +/// fn main() { +/// let exp2 = (*rand::random::<Exp1>()) * 0.5; +/// println(fmt!("%f is from a Exp(2) distribution", exp2)); +/// } +/// ~~~ +pub struct Exp1(f64); + +// This could be done via `-f64::ln(rng.gen::<f64>())` but that is slower. +impl Rand for Exp1 { + #[inline] + fn rand<R:Rng>(rng: &mut R) -> Exp1 { + #[inline(always)] + fn pdf(x: f64) -> f64 { + f64::exp(-x) + } + #[inline(always)] + fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 { + ziggurat_tables::ZIG_EXP_R - f64::ln(rng.gen()) + } + + Exp1(ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, &ziggurat_tables::ZIG_EXP_F_DIFF, + pdf, zero_case)) + } +} diff --git a/src/libstd/rand/ziggurat_tables.rs b/src/libstd/rand/ziggurat_tables.rs new file mode 100644 index 00000000000..aca2457cac4 --- /dev/null +++ b/src/libstd/rand/ziggurat_tables.rs @@ -0,0 +1,412 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +// Tables for distributions which are sampled using the ziggurat +// algorithm. Autogenerated by `ziggurat_tables.py`. + +pub type ZigTable = &'static [f64, .. 257]; +pub static ZIG_NORM_R: f64 = 3.654152885361008796; +pub static ZIG_NORM_X: [f64, .. 257] = + [3.910757959537090045, 3.654152885361008796, 3.449278298560964462, 3.320244733839166074, + 3.224575052047029100, 3.147889289517149969, 3.083526132001233044, 3.027837791768635434, + 2.978603279880844834, 2.934366867207854224, 2.894121053612348060, 2.857138730872132548, + 2.822877396825325125, 2.790921174000785765, 2.760944005278822555, 2.732685359042827056, + 2.705933656121858100, 2.680514643284522158, 2.656283037575502437, 2.633116393630324570, + 2.610910518487548515, 2.589575986706995181, 2.569035452680536569, 2.549221550323460761, + 2.530075232158516929, 2.511544441625342294, 2.493583041269680667, 2.476149939669143318, + 2.459208374333311298, 2.442725318198956774, 2.426670984935725972, 2.411018413899685520, + 2.395743119780480601, 2.380822795170626005, 2.366237056715818632, 2.351967227377659952, + 2.337996148795031370, 2.324308018869623016, 2.310888250599850036, 2.297723348901329565, + 2.284800802722946056, 2.272108990226823888, 2.259637095172217780, 2.247375032945807760, + 2.235313384928327984, 2.223443340090905718, 2.211756642882544366, 2.200245546609647995, + 2.188902771624720689, 2.177721467738641614, 2.166695180352645966, 2.155817819875063268, + 2.145083634046203613, 2.134487182844320152, 2.124023315687815661, 2.113687150684933957, + 2.103474055713146829, 2.093379631137050279, 2.083399693996551783, 2.073530263516978778, + 2.063767547809956415, 2.054107931648864849, 2.044547965215732788, 2.035084353727808715, + 2.025713947862032960, 2.016433734904371722, 2.007240830558684852, 1.998132471356564244, + 1.989106007615571325, 1.980158896898598364, 1.971288697931769640, 1.962493064942461896, + 1.953769742382734043, 1.945116560006753925, 1.936531428273758904, 1.928012334050718257, + 1.919557336591228847, 1.911164563769282232, 1.902832208548446369, 1.894558525668710081, + 1.886341828534776388, 1.878180486290977669, 1.870072921069236838, 1.862017605397632281, + 1.854013059758148119, 1.846057850283119750, 1.838150586580728607, 1.830289919680666566, + 1.822474540091783224, 1.814703175964167636, 1.806974591348693426, 1.799287584547580199, + 1.791640986550010028, 1.784033659547276329, 1.776464495522344977, 1.768932414909077933, + 1.761436365316706665, 1.753975320315455111, 1.746548278279492994, 1.739154261283669012, + 1.731792314050707216, 1.724461502945775715, 1.717160915015540690, 1.709889657069006086, + 1.702646854797613907, 1.695431651932238548, 1.688243209434858727, 1.681080704722823338, + 1.673943330923760353, 1.666830296159286684, 1.659740822855789499, 1.652674147080648526, + 1.645629517902360339, 1.638606196773111146, 1.631603456932422036, 1.624620582830568427, + 1.617656869570534228, 1.610711622367333673, 1.603784156023583041, 1.596873794420261339, + 1.589979870021648534, 1.583101723393471438, 1.576238702733332886, 1.569390163412534456, + 1.562555467528439657, 1.555733983466554893, 1.548925085471535512, 1.542128153226347553, + 1.535342571438843118, 1.528567729435024614, 1.521803020758293101, 1.515047842773992404, + 1.508301596278571965, 1.501563685112706548, 1.494833515777718391, 1.488110497054654369, + 1.481394039625375747, 1.474683555695025516, 1.467978458615230908, 1.461278162507407830, + 1.454582081885523293, 1.447889631277669675, 1.441200224845798017, 1.434513276002946425, + 1.427828197027290358, 1.421144398672323117, 1.414461289772464658, 1.407778276843371534, + 1.401094763676202559, 1.394410150925071257, 1.387723835686884621, 1.381035211072741964, + 1.374343665770030531, 1.367648583594317957, 1.360949343030101844, 1.354245316759430606, + 1.347535871177359290, 1.340820365893152122, 1.334098153216083604, 1.327368577624624679, + 1.320630975217730096, 1.313884673146868964, 1.307128989027353860, 1.300363230327433728, + 1.293586693733517645, 1.286798664489786415, 1.279998415710333237, 1.273185207661843732, + 1.266358287014688333, 1.259516886060144225, 1.252660221891297887, 1.245787495544997903, + 1.238897891102027415, 1.231990574742445110, 1.225064693752808020, 1.218119375481726552, + 1.211153726239911244, 1.204166830140560140, 1.197157747875585931, 1.190125515422801650, + 1.183069142678760732, 1.175987612011489825, 1.168879876726833800, 1.161744859441574240, + 1.154581450355851802, 1.147388505416733873, 1.140164844363995789, 1.132909248648336975, + 1.125620459211294389, 1.118297174115062909, 1.110938046009249502, 1.103541679420268151, + 1.096106627847603487, 1.088631390649514197, 1.081114409698889389, 1.073554065787871714, + 1.065948674757506653, 1.058296483326006454, 1.050595664586207123, 1.042844313139370538, + 1.035040439828605274, 1.027181966030751292, 1.019266717460529215, 1.011292417434978441, + 1.003256679539591412, 0.995156999629943084, 0.986990747093846266, 0.978755155288937750, + 0.970447311058864615, 0.962064143217605250, 0.953602409875572654, 0.945058684462571130, + 0.936429340280896860, 0.927710533396234771, 0.918898183643734989, 0.909987953490768997, + 0.900975224455174528, 0.891855070726792376, 0.882622229578910122, 0.873271068082494550, + 0.863795545546826915, 0.854189171001560554, 0.844444954902423661, 0.834555354079518752, + 0.824512208745288633, 0.814306670128064347, 0.803929116982664893, 0.793369058833152785, + 0.782615023299588763, 0.771654424216739354, 0.760473406422083165, 0.749056662009581653, + 0.737387211425838629, 0.725446140901303549, 0.713212285182022732, 0.700661841097584448, + 0.687767892786257717, 0.674499822827436479, 0.660822574234205984, 0.646695714884388928, + 0.632072236375024632, 0.616896989996235545, 0.601104617743940417, 0.584616766093722262, + 0.567338257040473026, 0.549151702313026790, 0.529909720646495108, 0.509423329585933393, + 0.487443966121754335, 0.463634336771763245, 0.437518402186662658, 0.408389134588000746, + 0.375121332850465727, 0.335737519180459465, 0.286174591747260509, 0.215241895913273806, + 0.000000000000000000]; +pub static ZIG_NORM_F: [f64, .. 257] = + [0.000477467764586655, 0.001260285930498598, 0.002609072746106363, 0.004037972593371872, + 0.005522403299264754, 0.007050875471392110, 0.008616582769422917, 0.010214971439731100, + 0.011842757857943104, 0.013497450601780807, 0.015177088307982072, 0.016880083152595839, + 0.018605121275783350, 0.020351096230109354, 0.022117062707379922, 0.023902203305873237, + 0.025705804008632656, 0.027527235669693315, 0.029365939758230111, 0.031221417192023690, + 0.033093219458688698, 0.034980941461833073, 0.036884215688691151, 0.038802707404656918, + 0.040736110656078753, 0.042684144916619378, 0.044646552251446536, 0.046623094902089664, + 0.048613553216035145, 0.050617723861121788, 0.052635418276973649, 0.054666461325077916, + 0.056710690106399467, 0.058767952921137984, 0.060838108349751806, 0.062921024437977854, + 0.065016577971470438, 0.067124653828023989, 0.069245144397250269, 0.071377949059141965, + 0.073522973714240991, 0.075680130359194964, 0.077849336702372207, 0.080030515814947509, + 0.082223595813495684, 0.084428509570654661, 0.086645194450867782, 0.088873592068594229, + 0.091113648066700734, 0.093365311913026619, 0.095628536713353335, 0.097903279039215627, + 0.100189498769172020, 0.102487158942306270, 0.104796225622867056, 0.107116667775072880, + 0.109448457147210021, 0.111791568164245583, 0.114145977828255210, 0.116511665626037014, + 0.118888613443345698, 0.121276805485235437, 0.123676228202051403, 0.126086870220650349, + 0.128508722280473636, 0.130941777174128166, 0.133386029692162844, 0.135841476571757352, + 0.138308116449064322, 0.140785949814968309, 0.143274978974047118, 0.145775208006537926, + 0.148286642733128721, 0.150809290682410169, 0.153343161060837674, 0.155888264725064563, + 0.158444614156520225, 0.161012223438117663, 0.163591108232982951, 0.166181285765110071, + 0.168782774801850333, 0.171395595638155623, 0.174019770082499359, 0.176655321444406654, + 0.179302274523530397, 0.181960655600216487, 0.184630492427504539, 0.187311814224516926, + 0.190004651671193070, 0.192709036904328807, 0.195425003514885592, 0.198152586546538112, + 0.200891822495431333, 0.203642749311121501, 0.206405406398679298, 0.209179834621935651, + 0.211966076307852941, 0.214764175252008499, 0.217574176725178370, 0.220396127481011589, + 0.223230075764789593, 0.226076071323264877, 0.228934165415577484, 0.231804410825248525, + 0.234686861873252689, 0.237581574432173676, 0.240488605941449107, 0.243408015423711988, + 0.246339863502238771, 0.249284212419516704, 0.252241126056943765, 0.255210669955677150, + 0.258192911338648023, 0.261187919133763713, 0.264195763998317568, 0.267216518344631837, + 0.270250256366959984, 0.273297054069675804, 0.276356989296781264, 0.279430141762765316, + 0.282516593084849388, 0.285616426816658109, 0.288729728483353931, 0.291856585618280984, + 0.294997087801162572, 0.298151326697901342, 0.301319396102034120, 0.304501391977896274, + 0.307697412505553769, 0.310907558127563710, 0.314131931597630143, 0.317370638031222396, + 0.320623784958230129, 0.323891482377732021, 0.327173842814958593, 0.330470981380537099, + 0.333783015832108509, 0.337110066638412809, 0.340452257045945450, 0.343809713148291340, + 0.347182563958251478, 0.350570941482881204, 0.353974980801569250, 0.357394820147290515, + 0.360830600991175754, 0.364282468130549597, 0.367750569780596226, 0.371235057669821344, + 0.374736087139491414, 0.378253817247238111, 0.381788410875031348, 0.385340034841733958, + 0.388908860020464597, 0.392495061461010764, 0.396098818517547080, 0.399720314981931668, + 0.403359739222868885, 0.407017284331247953, 0.410693148271983222, 0.414387534042706784, + 0.418100649839684591, 0.421832709231353298, 0.425583931339900579, 0.429354541031341519, + 0.433144769114574058, 0.436954852549929273, 0.440785034667769915, 0.444635565397727750, + 0.448506701509214067, 0.452398706863882505, 0.456311852680773566, 0.460246417814923481, + 0.464202689050278838, 0.468180961407822172, 0.472181538469883255, 0.476204732721683788, + 0.480250865911249714, 0.484320269428911598, 0.488413284707712059, 0.492530263646148658, + 0.496671569054796314, 0.500837575128482149, 0.505028667945828791, 0.509245245998136142, + 0.513487720749743026, 0.517756517232200619, 0.522052074674794864, 0.526374847174186700, + 0.530725304406193921, 0.535103932383019565, 0.539511234259544614, 0.543947731192649941, + 0.548413963257921133, 0.552910490428519918, 0.557437893621486324, 0.561996775817277916, + 0.566587763258951771, 0.571211506738074970, 0.575868682975210544, 0.580559996103683473, + 0.585286179266300333, 0.590047996335791969, 0.594846243770991268, 0.599681752622167719, + 0.604555390700549533, 0.609468064928895381, 0.614420723892076803, 0.619414360609039205, + 0.624450015550274240, 0.629528779928128279, 0.634651799290960050, 0.639820277456438991, + 0.645035480824251883, 0.650298743114294586, 0.655611470583224665, 0.660975147780241357, + 0.666391343912380640, 0.671861719900766374, 0.677388036222513090, 0.682972161648791376, + 0.688616083008527058, 0.694321916130032579, 0.700091918140490099, 0.705928501336797409, + 0.711834248882358467, 0.717811932634901395, 0.723864533472881599, 0.729995264565802437, + 0.736207598131266683, 0.742505296344636245, 0.748892447223726720, 0.755373506511754500, + 0.761953346841546475, 0.768637315803334831, 0.775431304986138326, 0.782341832659861902, + 0.789376143571198563, 0.796542330428254619, 0.803849483176389490, 0.811307874318219935, + 0.818929191609414797, 0.826726833952094231, 0.834716292992930375, 0.842915653118441077, + 0.851346258465123684, 0.860033621203008636, 0.869008688043793165, 0.878309655816146839, + 0.887984660763399880, 0.898095921906304051, 0.908726440060562912, 0.919991505048360247, + 0.932060075968990209, 0.945198953453078028, 0.959879091812415930, 0.977101701282731328, + 1.000000000000000000]; +pub static ZIG_NORM_F_DIFF: [f64, .. 257] = + [0.000000000000000000, 0.000782818165911943, 0.001348786815607765, 0.001428899847265509, + 0.001484430705892882, 0.001528472172127356, 0.001565707298030807, 0.001598388670308183, + 0.001627786418212004, 0.001654692743837703, 0.001679637706201265, 0.001702994844613767, + 0.001725038123187510, 0.001745974954326004, 0.001765966477270568, 0.001785140598493315, + 0.001803600702759419, 0.001821431661060659, 0.001838704088536796, 0.001855477433793579, + 0.001871802266665008, 0.001887722003144375, 0.001903274226858077, 0.001918491715965767, + 0.001933403251421835, 0.001948034260540625, 0.001962407334827158, 0.001976542650643127, + 0.001990458313945481, 0.002004170645086643, 0.002017694415851860, 0.002031043048104267, + 0.002044228781321551, 0.002057262814738517, 0.002070155428613822, 0.002082916088226049, + 0.002095553533492583, 0.002108075856553551, 0.002120490569226280, 0.002132804661891696, + 0.002145024655099026, 0.002157156644953973, 0.002169206343177243, 0.002181179112575302, + 0.002193079998548175, 0.002204913757158977, 0.002216684880213121, 0.002228397617726446, + 0.002240055998106505, 0.002251663846325885, 0.002263224800326716, 0.002274742325862292, + 0.002286219729956393, 0.002297660173134250, 0.002309066680560787, 0.002320442152205823, + 0.002331789372137141, 0.002343111017035562, 0.002354409664009627, 0.002365687797781804, + 0.002376947817308683, 0.002388192041889739, 0.002399422716815966, 0.002410642018598946, + 0.002421852059823287, 0.002433054893654529, 0.002444252518034679, 0.002455446879594508, + 0.002466639877306970, 0.002477833365903986, 0.002489029159078809, 0.002500229032490808, + 0.002511434726590794, 0.002522647949281448, 0.002533870378427505, 0.002545103664226889, + 0.002556349431455662, 0.002567609281597438, 0.002578884794865288, 0.002590177532127119, + 0.002601489036740262, 0.002612820836305291, 0.002624174444343735, 0.002635551361907296, + 0.002646953079123743, 0.002658381076686089, 0.002669836827288052, 0.002681321797012387, + 0.002692837446676144, 0.002704385233135737, 0.002715966610556786, 0.002727583031652520, + 0.002739235948893221, 0.002750926815690169, 0.002762657087557796, 0.002774428223256353, + 0.002786241685917290, 0.002798098944155558, 0.002810001473169871, 0.002821950755833219, + 0.002833948283778004, 0.002845995558475284, 0.002858094092312607, 0.002870245409671041, + 0.002882451048004164, 0.002894712558920987, 0.002907031509275432, 0.002919409482262880, + 0.002931848078526783, 0.002944348917277934, 0.002956913637427061, 0.002969543898733384, + 0.002982241382970874, 0.002995007795115689, 0.003007844864553855, 0.003020754346314269, + 0.003033738022328147, 0.003046797702715820, 0.003059935227105459, 0.003073152465984053, + 0.003086451322084072, 0.003099833731808721, 0.003113301666695822, 0.003126857134927052, + 0.003140502182881588, 0.003154238896738770, 0.003168069404132778, 0.003181995875862154, + 0.003196020527657495, 0.003210145622009941, 0.003224373470066433, 0.003238706433592253, + 0.003253146927007733, 0.003267697419501892, 0.003282360437226572, 0.003297138565578506, + 0.003312034451571411, 0.003327050806304299, 0.003342190407532641, 0.003357456102345890, + 0.003372850809960137, 0.003388377524629727, 0.003404039318688046, 0.003419839345721265, + 0.003435780843885239, 0.003451867139373843, 0.003468101650046629, 0.003484487889225119, + 0.003501029469670069, 0.003517730107746697, 0.003534593627793237, 0.003551623966702611, + 0.003568825178730639, 0.003586201440546166, 0.003603757056536316, 0.003621496464384588, + 0.003639424240937217, 0.003657545108379068, 0.003675863940735269, 0.003694385770723563, + 0.003713115796977806, 0.003732059391668707, 0.003751222108547281, 0.003770609691440940, + 0.003790228083232539, 0.003810083435355216, 0.003830182117840641, 0.003850530729957835, + 0.003871136111486317, 0.003892005354668437, 0.003913145816891062, 0.003934565134149914, + 0.003956271235355358, 0.003978272357543333, 0.004000577062061084, 0.004023194251800533, + 0.004046133189565926, 0.004069403517661885, 0.004093015278800460, 0.004116978938436600, + 0.004141305408647655, 0.004166006073685835, 0.004191092817346642, 0.004216578052307351, + 0.004242474751606884, 0.004268796482457593, 0.004295557442594244, 0.004322772499391836, + 0.004350457232007221, 0.004378627976825644, 0.004407301876525049, 0.004436496933105327, + 0.004466232065271192, 0.004496527170598785, 0.004527403192966406, 0.004558882195791591, + 0.004590987441673855, 0.004623743479123199, 0.004657176237135574, 0.004691313128472929, + 0.004726183162616859, 0.004761817069491636, 0.004798247435199299, 0.004835508851176451, + 0.004873638078381815, 0.004912674228345848, 0.004952658963181422, 0.004993636716962402, + 0.005035654941235035, 0.005078764377854039, 0.005123019362831771, 0.005168478165478940, + 0.005215203367812893, 0.005263262290042703, 0.005312727468930079, 0.005363677197016692, + 0.005416196132139284, 0.005470375988385734, 0.005526316321746716, 0.005584125426278286, + 0.005643921359735682, 0.005705833121505521, 0.005770002010457520, 0.005836583196307310, + 0.005905747545561058, 0.005977683752542928, 0.006052600837980204, 0.006130731092920838, + 0.006212333565464245, 0.006297698213369562, 0.006387150879090475, 0.006481059288027780, + 0.006579840329791975, 0.006683968961788356, 0.006793989182803495, 0.006910527673723577, + 0.007034310911336661, 0.007166186857056056, 0.007307152748134871, 0.007458391141830445, + 0.007621317291194862, 0.007797642342679434, 0.007989459040836144, 0.008199360125510702, + 0.008430605346682607, 0.008687362737884952, 0.008975066840784529, 0.009300967772353674, + 0.009675004947253041, 0.010111261142904171, 0.010630518154258861, 0.011265064987797335, + 0.012068570920629962, 0.013138877484087819, 0.014680138359337902, 0.017222609470315398, + 0.022898298717268672]; +pub static ZIG_EXP_R: f64 = 7.697117470131050077; +pub static ZIG_EXP_X: [f64, .. 257] = + [8.697117470131052741, 7.697117470131050077, 6.941033629377212577, 6.478378493832569696, + 6.144164665772472667, 5.882144315795399869, 5.666410167454033697, 5.482890627526062488, + 5.323090505754398016, 5.181487281301500047, 5.054288489981304089, 4.938777085901250530, + 4.832939741025112035, 4.735242996601741083, 4.644491885420085175, 4.559737061707351380, + 4.480211746528421912, 4.405287693473573185, 4.334443680317273007, 4.267242480277365857, + 4.203313713735184365, 4.142340865664051464, 4.084051310408297830, 4.028208544647936762, + 3.974606066673788796, 3.923062500135489739, 3.873417670399509127, 3.825529418522336744, + 3.779270992411667862, 3.734528894039797375, 3.691201090237418825, 3.649195515760853770, + 3.608428813128909507, 3.568825265648337020, 3.530315889129343354, 3.492837654774059608, + 3.456332821132760191, 3.420748357251119920, 3.386035442460300970, 3.352149030900109405, + 3.319047470970748037, 3.286692171599068679, 3.255047308570449882, 3.224079565286264160, + 3.193757903212240290, 3.164053358025972873, 3.134938858084440394, 3.106389062339824481, + 3.078380215254090224, 3.050890016615455114, 3.023897504455676621, 2.997382949516130601, + 2.971327759921089662, 2.945714394895045718, 2.920526286512740821, 2.895747768600141825, + 2.871364012015536371, 2.847360965635188812, 2.823725302450035279, 2.800444370250737780, + 2.777506146439756574, 2.754899196562344610, 2.732612636194700073, 2.710636095867928752, + 2.688959688741803689, 2.667573980773266573, 2.646469963151809157, 2.625639026797788489, + 2.605072938740835564, 2.584763820214140750, 2.564704126316905253, 2.544886627111869970, + 2.525304390037828028, 2.505950763528594027, 2.486819361740209455, 2.467904050297364815, + 2.449198932978249754, 2.430698339264419694, 2.412396812688870629, 2.394289099921457886, + 2.376370140536140596, 2.358635057409337321, 2.341079147703034380, 2.323697874390196372, + 2.306486858283579799, 2.289441870532269441, 2.272558825553154804, 2.255833774367219213, + 2.239262898312909034, 2.222842503111036816, 2.206569013257663858, 2.190438966723220027, + 2.174449009937774679, 2.158595893043885994, 2.142876465399842001, 2.127287671317368289, + 2.111826546019042183, 2.096490211801715020, 2.081275874393225145, 2.066180819490575526, + 2.051202409468584786, 2.036338080248769611, 2.021585338318926173, 2.006941757894518563, + 1.992404978213576650, 1.977972700957360441, 1.963642687789548313, 1.949412758007184943, + 1.935280786297051359, 1.921244700591528076, 1.907302480018387536, 1.893452152939308242, + 1.879691795072211180, 1.866019527692827973, 1.852433515911175554, 1.838931967018879954, + 1.825513128903519799, 1.812175288526390649, 1.798916770460290859, 1.785735935484126014, + 1.772631179231305643, 1.759600930889074766, 1.746643651946074405, 1.733757834985571566, + 1.720942002521935299, 1.708194705878057773, 1.695514524101537912, 1.682900062917553896, + 1.670349953716452118, 1.657862852574172763, 1.645437439303723659, 1.633072416535991334, + 1.620766508828257901, 1.608518461798858379, 1.596327041286483395, 1.584191032532688892, + 1.572109239386229707, 1.560080483527888084, 1.548103603714513499, 1.536177455041032092, + 1.524300908219226258, 1.512472848872117082, 1.500692176842816750, 1.488957805516746058, + 1.477268661156133867, 1.465623682245745352, 1.454021818848793446, 1.442462031972012504, + 1.430943292938879674, 1.419464582769983219, 1.408024891569535697, 1.396623217917042137, + 1.385258568263121992, 1.373929956328490576, 1.362636402505086775, 1.351376933258335189, + 1.340150580529504643, 1.328956381137116560, 1.317793376176324749, 1.306660610415174117, + 1.295557131686601027, 1.284481990275012642, 1.273434238296241139, 1.262412929069615330, + 1.251417116480852521, 1.240445854334406572, 1.229498195693849105, 1.218573192208790124, + 1.207669893426761121, 1.196787346088403092, 1.185924593404202199, 1.175080674310911677, + 1.164254622705678921, 1.153445466655774743, 1.142652227581672841, 1.131873919411078511, + 1.121109547701330200, 1.110358108727411031, 1.099618588532597308, 1.088889961938546813, + 1.078171191511372307, 1.067461226479967662, 1.056759001602551429, 1.046063435977044209, + 1.035373431790528542, 1.024687873002617211, 1.014005623957096480, 1.003325527915696735, + 0.992646405507275897, 0.981967053085062602, 0.971286240983903260, 0.960602711668666509, + 0.949915177764075969, 0.939222319955262286, 0.928522784747210395, 0.917815182070044311, + 0.907098082715690257, 0.896370015589889935, 0.885629464761751528, 0.874874866291025066, + 0.864104604811004484, 0.853317009842373353, 0.842510351810368485, 0.831682837734273206, + 0.820832606554411814, 0.809957724057418282, 0.799056177355487174, 0.788125868869492430, + 0.777164609759129710, 0.766170112735434672, 0.755139984181982249, 0.744071715500508102, + 0.732962673584365398, 0.721810090308756203, 0.710611050909655040, 0.699362481103231959, + 0.688061132773747808, 0.676703568029522584, 0.665286141392677943, 0.653804979847664947, + 0.642255960424536365, 0.630634684933490286, 0.618936451394876075, 0.607156221620300030, + 0.595288584291502887, 0.583327712748769489, 0.571267316532588332, 0.559100585511540626, + 0.546820125163310577, 0.534417881237165604, 0.521885051592135052, 0.509211982443654398, + 0.496388045518671162, 0.483401491653461857, 0.470239275082169006, 0.456886840931420235, + 0.443327866073552401, 0.429543940225410703, 0.415514169600356364, 0.401214678896277765, + 0.386617977941119573, 0.371692145329917234, 0.356399760258393816, 0.340696481064849122, + 0.324529117016909452, 0.307832954674932158, 0.290527955491230394, 0.272513185478464703, + 0.253658363385912022, 0.233790483059674731, 0.212671510630966620, 0.189958689622431842, + 0.165127622564187282, 0.137304980940012589, 0.104838507565818778, 0.063852163815001570, + 0.000000000000000000]; +pub static ZIG_EXP_F: [f64, .. 257] = + [0.000167066692307963, 0.000454134353841497, 0.000967269282327174, 0.001536299780301573, + 0.002145967743718907, 0.002788798793574076, 0.003460264777836904, 0.004157295120833797, + 0.004877655983542396, 0.005619642207205489, 0.006381905937319183, 0.007163353183634991, + 0.007963077438017043, 0.008780314985808977, 0.009614413642502212, 0.010464810181029981, + 0.011331013597834600, 0.012212592426255378, 0.013109164931254991, 0.014020391403181943, + 0.014945968011691148, 0.015885621839973156, 0.016839106826039941, 0.017806200410911355, + 0.018786700744696024, 0.019780424338009740, 0.020787204072578114, 0.021806887504283581, + 0.022839335406385240, 0.023884420511558174, 0.024942026419731787, 0.026012046645134221, + 0.027094383780955803, 0.028188948763978646, 0.029295660224637411, 0.030414443910466622, + 0.031545232172893622, 0.032687963508959555, 0.033842582150874358, 0.035009037697397431, + 0.036187284781931443, 0.037377282772959382, 0.038578995503074871, 0.039792391023374139, + 0.041017441380414840, 0.042254122413316254, 0.043502413568888197, 0.044762297732943289, + 0.046033761076175184, 0.047316792913181561, 0.048611385573379504, 0.049917534282706379, + 0.051235237055126281, 0.052564494593071685, 0.053905310196046080, 0.055257689676697030, + 0.056621641283742870, 0.057997175631200659, 0.059384305633420280, 0.060783046445479660, + 0.062193415408541036, 0.063615431999807376, 0.065049117786753805, 0.066494496385339816, + 0.067951593421936643, 0.069420436498728783, 0.070901055162371843, 0.072393480875708752, + 0.073897746992364746, 0.075413888734058410, 0.076941943170480517, 0.078481949201606435, + 0.080033947542319905, 0.081597980709237419, 0.083174093009632397, 0.084762330532368146, + 0.086362741140756927, 0.087975374467270231, 0.089600281910032886, 0.091237516631040197, + 0.092887133556043569, 0.094549189376055873, 0.096223742550432825, 0.097910853311492213, + 0.099610583670637132, 0.101322997425953631, 0.103048160171257702, 0.104786139306570145, + 0.106537004050001632, 0.108300825451033755, 0.110077676405185357, 0.111867631670056283, + 0.113670767882744286, 0.115487163578633506, 0.117316899211555525, 0.119160057175327641, + 0.121016721826674792, 0.122886979509545108, 0.124770918580830933, 0.126668629437510671, + 0.128580204545228199, 0.130505738468330773, 0.132445327901387494, 0.134399071702213602, + 0.136367070926428829, 0.138349428863580176, 0.140346251074862399, 0.142357645432472146, + 0.144383722160634720, 0.146424593878344889, 0.148480375643866735, 0.150551185001039839, + 0.152637142027442801, 0.154738369384468027, 0.156854992369365148, 0.158987138969314129, + 0.161134939917591952, 0.163298528751901734, 0.165478041874935922, 0.167673618617250081, + 0.169885401302527550, 0.172113535315319977, 0.174358169171353411, 0.176619454590494829, + 0.178897546572478278, 0.181192603475496261, 0.183504787097767436, 0.185834262762197083, + 0.188181199404254262, 0.190545769663195363, 0.192928149976771296, 0.195328520679563189, + 0.197747066105098818, 0.200183974691911210, 0.202639439093708962, 0.205113656293837654, + 0.207606827724221982, 0.210119159388988230, 0.212650861992978224, 0.215202151075378628, + 0.217773247148700472, 0.220364375843359439, 0.222975768058120111, 0.225607660116683956, + 0.228260293930716618, 0.230933917169627356, 0.233628783437433291, 0.236345152457059560, + 0.239083290262449094, 0.241843469398877131, 0.244625969131892024, 0.247431075665327543, + 0.250259082368862240, 0.253110290015629402, 0.255985007030415324, 0.258883549749016173, + 0.261806242689362922, 0.264753418835062149, 0.267725419932044739, 0.270722596799059967, + 0.273745309652802915, 0.276793928448517301, 0.279868833236972869, 0.282970414538780746, + 0.286099073737076826, 0.289255223489677693, 0.292439288161892630, 0.295651704281261252, + 0.298892921015581847, 0.302163400675693528, 0.305463619244590256, 0.308794066934560185, + 0.312155248774179606, 0.315547685227128949, 0.318971912844957239, 0.322428484956089223, + 0.325917972393556354, 0.329440964264136438, 0.332998068761809096, 0.336589914028677717, + 0.340217149066780189, 0.343880444704502575, 0.347580494621637148, 0.351318016437483449, + 0.355093752866787626, 0.358908472948750001, 0.362762973354817997, 0.366658079781514379, + 0.370594648435146223, 0.374573567615902381, 0.378595759409581067, 0.382662181496010056, + 0.386773829084137932, 0.390931736984797384, 0.395136981833290435, 0.399390684475231350, + 0.403694012530530555, 0.408048183152032673, 0.412454465997161457, 0.416914186433003209, + 0.421428728997616908, 0.425999541143034677, 0.430628137288459167, 0.435316103215636907, + 0.440065100842354173, 0.444876873414548846, 0.449753251162755330, 0.454696157474615836, + 0.459707615642138023, 0.464789756250426511, 0.469944825283960310, 0.475175193037377708, + 0.480483363930454543, 0.485871987341885248, 0.491343869594032867, 0.496901987241549881, + 0.502549501841348056, 0.508289776410643213, 0.514126393814748894, 0.520063177368233931, + 0.526104213983620062, 0.532253880263043655, 0.538516872002862246, 0.544898237672440056, + 0.551403416540641733, 0.558038282262587892, 0.564809192912400615, 0.571723048664826150, + 0.578787358602845359, 0.586010318477268366, 0.593400901691733762, 0.600968966365232560, + 0.608725382079622346, 0.616682180915207878, 0.624852738703666200, 0.633251994214366398, + 0.641896716427266423, 0.650805833414571433, 0.660000841079000145, 0.669506316731925177, + 0.679350572264765806, 0.689566496117078431, 0.700192655082788606, 0.711274760805076456, + 0.722867659593572465, 0.735038092431424039, 0.747868621985195658, 0.761463388849896838, + 0.775956852040116218, 0.791527636972496285, 0.808421651523009044, 0.826993296643051101, + 0.847785500623990496, 0.871704332381204705, 0.900469929925747703, 0.938143680862176477, + 1.000000000000000000]; +pub static ZIG_EXP_F_DIFF: [f64, .. 257] = + [0.000000000000000000, 0.000287067661533533, 0.000513134928485678, 0.000569030497974398, + 0.000609667963417335, 0.000642831049855169, 0.000671465984262828, 0.000697030342996893, + 0.000720360862708599, 0.000741986223663093, 0.000762263730113694, 0.000781447246315807, + 0.000799724254382053, 0.000817237547791934, 0.000834098656693235, 0.000850396538527769, + 0.000866203416804620, 0.000881578828420777, 0.000896572504999613, 0.000911226471926952, + 0.000925576608509206, 0.000939653828282008, 0.000953484986066785, 0.000967093584871414, + 0.000980500333784669, 0.000993723593313716, 0.001006779734568374, 0.001019683431705467, + 0.001032447902101660, 0.001045085105172934, 0.001057605908173612, 0.001070020225402434, + 0.001082337135821582, 0.001094564983022843, 0.001106711460658764, 0.001118783685829211, + 0.001130788262427001, 0.001142731336065933, 0.001154618641914802, 0.001166455546523074, + 0.001178247084534012, 0.001189997991027938, 0.001201712730115490, 0.001213395520299268, + 0.001225050357040701, 0.001236681032901414, 0.001248291155571943, 0.001259884164055092, + 0.001271463343231895, 0.001283031837006378, 0.001294592660197942, 0.001306148709326875, + 0.001317702772419903, 0.001329257537945404, 0.001340815602974395, 0.001352379480650950, + 0.001363951607045839, 0.001375534347457789, 0.001387130002219621, 0.001398740812059381, + 0.001410368963061376, 0.001422016591266340, 0.001433685786946429, 0.001445378598586011, + 0.001457097036596827, 0.001468843076792140, 0.001480618663643060, 0.001492425713336909, + 0.001504266116655995, 0.001516141741693663, 0.001528054436422108, 0.001540006031125918, + 0.001551998340713470, 0.001564033166917514, 0.001576112300394977, 0.001588237522735750, + 0.001600410608388780, 0.001612633326513305, 0.001624907442762655, 0.001637234721007311, + 0.001649616925003372, 0.001662055820012304, 0.001674553174376953, 0.001687110761059388, + 0.001699730359144919, 0.001712413755316500, 0.001725162745304071, 0.001737979135312442, + 0.001750864743431488, 0.001763821401032123, 0.001776850954151601, 0.001789955264870927, + 0.001803136212688003, 0.001816395695889220, 0.001829735632922019, 0.001843157963772116, + 0.001856664651347151, 0.001870257682870316, 0.001883939071285826, 0.001897710856679738, + 0.001911575107717528, 0.001925533923102574, 0.001939589433056721, 0.001953743800826108, + 0.001967999224215228, 0.001982357937151347, 0.001996822211282223, 0.002011394357609747, + 0.002026076728162574, 0.002040871717710169, 0.002055781765521847, 0.002070809357173103, + 0.002085957026402963, 0.002101227357025226, 0.002116622984897121, 0.002132146599948981, + 0.002147800948277823, 0.002163588834309782, 0.002179513123034188, 0.002195576742314159, + 0.002211782685277469, 0.002228134012792427, 0.002244633856033434, 0.002261285419141418, + 0.002278091981983449, 0.002295056903017983, 0.002312183622271174, 0.002329475664429648, + 0.002346936642057179, 0.002364570258941101, 0.002382380313575932, 0.002400370702791893, + 0.002418545425535629, 0.002436908586812392, 0.002455464401797752, 0.002474217200128692, + 0.002493171430384328, 0.002512331664766249, 0.002531702603989994, 0.002551289082400404, + 0.002571096073321844, 0.002591128694658967, 0.002611392214760672, 0.002631892058563845, + 0.002652633814032662, 0.002673623238910738, 0.002694866267805934, 0.002716369019626269, + 0.002738137805389534, 0.002760179136428037, 0.002782499733014893, 0.002805106533435520, + 0.002828006703534697, 0.002851207646767162, 0.002874717014785921, 0.002898542718600849, + 0.002922692940346749, 0.002947176145699226, 0.002972001096982591, 0.002997176867015228, + 0.003022712853742948, 0.003048618795714386, 0.003074904788455568, 0.003101581301807876, + 0.003128659198296080, 0.003156149752600867, 0.003184064672214937, 0.003212416119368622, + 0.003241216734320596, 0.003270479660111680, 0.003300218568896729, 0.003330447689969929, + 0.003361181839619420, 0.003392436452949343, 0.003424227617828290, 0.003456572111131984, + 0.003489487437467131, 0.003522991870580083, 0.003557104497672658, 0.003591845266868621, + 0.003627235038102472, 0.003663295637722386, 0.003700049917134574, 0.003737521815846301, + 0.003775736429304177, 0.003814720081962375, 0.003854500406067995, 0.003895106426696382, + 0.003936568653631844, 0.003978919180756157, 0.004022191793678687, 0.004066422086428989, + 0.004111647588127876, 0.004157907900659452, 0.004205244848493050, 0.004253702641940915, + 0.004303328055299205, 0.004354170621502118, 0.004406282845128784, 0.004459720435841752, + 0.004514542564613699, 0.004570812145417769, 0.004628596145424491, 0.004687965927177740, + 0.004748997626717266, 0.004811772572194672, 0.004876377748206484, 0.004942906311860507, + 0.005011458167522187, 0.005082140608288488, 0.005155069033533799, 0.005230367753417398, + 0.005308170893076836, 0.005388623411430704, 0.005471882252147620, 0.005558117647517014, + 0.005647514599798176, 0.005740274569295156, 0.005836617404105682, 0.005936783553485037, + 0.006041036615386131, 0.006149666279423593, 0.006262991739818591, 0.006381365669577810, + 0.006505178868201678, 0.006634865721946159, 0.006770910649812723, 0.006913855752425535, + 0.007064309938019209, 0.007222959874423007, 0.007390583214465396, 0.007568064673498798, + 0.007756415714389786, 0.007956798835585532, 0.008170557788458321, 0.008399255510700199, + 0.008644722212900025, 0.008909116987305010, 0.009195007664428712, 0.009505475652925033, + 0.009844255532840629, 0.010215923852312625, 0.010626158965710175, 0.011082105722287849, + 0.011592898788496009, 0.012170432837851575, 0.012830529553771619, 0.013594766864701180, + 0.014493463190219380, 0.015570784932380066, 0.016894014550512759, 0.018571645120042057, + 0.020792203980939394, 0.023918831757214210, 0.028765597544542998, 0.037673750936428774, + 0.061856319137823523]; diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs deleted file mode 100644 index 02f824b9113..00000000000 --- a/src/libstd/rc.rs +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/** Task-local reference counted smart pointers - -Task-local reference counted smart pointers are an alternative to managed boxes with deterministic -destruction. They are restricted to containing types that are either `Owned` or `Const` (or both) to -prevent cycles. - -Neither `Rc<T>` or `RcMut<T>` is ever `Owned` and `RcMut<T>` is never `Const`. If `T` is `Const`, a -cycle cannot be created with `Rc<T>` because there is no way to modify it after creation. - -*/ - -use core::libc::{c_void, size_t, malloc, free}; -use core::unstable::intrinsics; -use core::util; - -struct RcBox<T> { - value: T, - count: uint -} - -/// Immutable reference counted pointer type -#[non_owned] -pub struct Rc<T> { - priv ptr: *mut RcBox<T>, -} - -priv impl<T> Rc<T> { - unsafe fn new(value: T) -> Rc<T> { - let ptr = malloc(sys::size_of::<RcBox<T>>() as size_t) as *mut RcBox<T>; - assert!(!ptr::is_null(ptr)); - intrinsics::move_val_init(&mut *ptr, RcBox{value: value, count: 1}); - Rc{ptr: ptr} - } -} - -// FIXME: #6516: should be a static method -pub fn rc_from_owned<T: Owned>(value: T) -> Rc<T> { - unsafe { Rc::new(value) } -} - -// FIXME: #6516: should be a static method -pub fn rc_from_const<T: Const>(value: T) -> Rc<T> { - unsafe { Rc::new(value) } -} - -pub impl<T> Rc<T> { - #[inline(always)] - fn borrow<'r>(&'r self) -> &'r T { - unsafe { cast::copy_lifetime(self, &(*self.ptr).value) } - } -} - -#[unsafe_destructor] -impl<T> Drop for Rc<T> { - fn finalize(&self) { - unsafe { - (*self.ptr).count -= 1; - if (*self.ptr).count == 0 { - util::replace_ptr(self.ptr, intrinsics::uninit()); - free(self.ptr as *c_void) - } - } - } -} - -impl<T> Clone for Rc<T> { - /// Return a shallow copy of the reference counted pointer. - #[inline] - fn clone(&self) -> Rc<T> { - unsafe { - (*self.ptr).count += 1; - Rc{ptr: self.ptr} - } - } -} - -impl<T: DeepClone> DeepClone for Rc<T> { - /// Return a deep copy of the reference counted pointer. - #[inline] - fn deep_clone(&self) -> Rc<T> { - unsafe { Rc::new(self.borrow().deep_clone()) } - } -} - -#[cfg(test)] -mod test_rc { - use super::*; - use core::cell::Cell; - - #[test] - fn test_clone() { - let x = rc_from_owned(Cell(5)); - let y = x.clone(); - do x.borrow().with_mut_ref |inner| { - *inner = 20; - } - assert_eq!(y.borrow().take(), 20); - } - - #[test] - fn test_deep_clone() { - let x = rc_from_owned(Cell(5)); - let y = x.deep_clone(); - do x.borrow().with_mut_ref |inner| { - *inner = 20; - } - assert_eq!(y.borrow().take(), 5); - } - - #[test] - fn test_simple() { - let x = rc_from_const(5); - assert_eq!(*x.borrow(), 5); - } - - #[test] - fn test_simple_clone() { - let x = rc_from_const(5); - let y = x.clone(); - assert_eq!(*x.borrow(), 5); - assert_eq!(*y.borrow(), 5); - } - - #[test] - fn test_destructor() { - let x = rc_from_owned(~5); - assert_eq!(**x.borrow(), 5); - } -} - -#[abi = "rust-intrinsic"] -extern "rust-intrinsic" { - fn init<T>() -> T; - fn uninit<T>() -> T; -} - -#[deriving(Eq)] -enum Borrow { - Mutable, - Immutable, - Nothing -} - -struct RcMutBox<T> { - value: T, - count: uint, - borrow: Borrow -} - -/// Mutable reference counted pointer type -#[non_owned] -#[mutable] -pub struct RcMut<T> { - priv ptr: *mut RcMutBox<T>, -} - -priv impl<T> RcMut<T> { - unsafe fn new(value: T) -> RcMut<T> { - let ptr = malloc(sys::size_of::<RcMutBox<T>>() as size_t) as *mut RcMutBox<T>; - assert!(!ptr::is_null(ptr)); - intrinsics::move_val_init(&mut *ptr, RcMutBox{value: value, count: 1, borrow: Nothing}); - RcMut{ptr: ptr} - } -} - -// FIXME: #6516: should be a static method -pub fn rc_mut_from_owned<T: Owned>(value: T) -> RcMut<T> { - unsafe { RcMut::new(value) } -} - -// FIXME: #6516: should be a static method -pub fn rc_mut_from_const<T: Const>(value: T) -> RcMut<T> { - unsafe { RcMut::new(value) } -} - -pub impl<T> RcMut<T> { - /// Fails if there is already a mutable borrow of the box - #[inline] - fn with_borrow<U>(&self, f: &fn(&T) -> U) -> U { - unsafe { - assert!((*self.ptr).borrow != Mutable); - let previous = (*self.ptr).borrow; - (*self.ptr).borrow = Immutable; - let res = f(&(*self.ptr).value); - (*self.ptr).borrow = previous; - res - } - } - - /// Fails if there is already a mutable or immutable borrow of the box - #[inline] - fn with_mut_borrow<U>(&self, f: &fn(&mut T) -> U) -> U { - unsafe { - assert_eq!((*self.ptr).borrow, Nothing); - (*self.ptr).borrow = Mutable; - let res = f(&mut (*self.ptr).value); - (*self.ptr).borrow = Nothing; - res - } - } -} - -#[unsafe_destructor] -impl<T> Drop for RcMut<T> { - fn finalize(&self) { - unsafe { - (*self.ptr).count -= 1; - if (*self.ptr).count == 0 { - util::replace_ptr(self.ptr, uninit()); - free(self.ptr as *c_void) - } - } - } -} - -impl<T> Clone for RcMut<T> { - /// Return a shallow copy of the reference counted pointer. - #[inline] - fn clone(&self) -> RcMut<T> { - unsafe { - (*self.ptr).count += 1; - RcMut{ptr: self.ptr} - } - } -} - -impl<T: DeepClone> DeepClone for RcMut<T> { - /// Return a deep copy of the reference counted pointer. - #[inline] - fn deep_clone(&self) -> RcMut<T> { - do self.with_borrow |x| { - // FIXME: #6497: should avoid freeze (slow) - unsafe { RcMut::new(x.deep_clone()) } - } - } -} - -#[cfg(test)] -mod test_rc_mut { - use super::*; - - #[test] - fn test_clone() { - let x = rc_mut_from_owned(5); - let y = x.clone(); - do x.with_mut_borrow |value| { - *value = 20; - } - do y.with_borrow |value| { - assert_eq!(*value, 20); - } - } - - #[test] - fn test_deep_clone() { - let x = rc_mut_from_const(5); - let y = x.deep_clone(); - do x.with_mut_borrow |value| { - *value = 20; - } - do y.with_borrow |value| { - assert_eq!(*value, 5); - } - } - - #[test] - fn borrow_many() { - let x = rc_mut_from_owned(5); - let y = x.clone(); - - do x.with_borrow |a| { - assert_eq!(*a, 5); - do y.with_borrow |b| { - assert_eq!(*b, 5); - do x.with_borrow |c| { - assert_eq!(*c, 5); - } - } - } - } - - #[test] - fn modify() { - let x = rc_mut_from_const(5); - let y = x.clone(); - - do y.with_mut_borrow |a| { - assert_eq!(*a, 5); - *a = 6; - } - - do x.with_borrow |a| { - assert_eq!(*a, 6); - } - } - - #[test] - fn release_immutable() { - let x = rc_mut_from_owned(5); - do x.with_borrow |_| {} - do x.with_mut_borrow |_| {} - } - - #[test] - fn release_mutable() { - let x = rc_mut_from_const(5); - do x.with_mut_borrow |_| {} - do x.with_borrow |_| {} - } - - #[test] - #[should_fail] - fn frozen() { - let x = rc_mut_from_owned(5); - let y = x.clone(); - - do x.with_borrow |_| { - do y.with_mut_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn mutable_dupe() { - let x = rc_mut_from_const(5); - let y = x.clone(); - - do x.with_mut_borrow |_| { - do y.with_mut_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn mutable_freeze() { - let x = rc_mut_from_owned(5); - let y = x.clone(); - - do x.with_mut_borrow |_| { - do y.with_borrow |_| { - } - } - } - - #[test] - #[should_fail] - fn restore_freeze() { - let x = rc_mut_from_const(5); - let y = x.clone(); - - do x.with_borrow |_| { - do x.with_borrow |_| {} - do y.with_mut_borrow |_| {} - } - } -} diff --git a/src/libstd/reflect.rs b/src/libstd/reflect.rs new file mode 100644 index 00000000000..30f60dce041 --- /dev/null +++ b/src/libstd/reflect.rs @@ -0,0 +1,500 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Runtime type reflection + +*/ + +use intrinsic::{TyDesc, TyVisitor}; +use intrinsic::Opaque; +use libc::c_void; +use sys; +use vec; + +/** + * Trait for visitor that wishes to reflect on data. To use this, create a + * struct that encapsulates the set of pointers you wish to walk through a + * data structure, and implement both `MovePtr` for it as well as `TyVisitor`; + * then build a MovePtrAdaptor wrapped around your struct. + */ +pub trait MovePtr { + fn move_ptr(&self, adjustment: &fn(*c_void) -> *c_void); + fn push_ptr(&self); + fn pop_ptr(&self); +} + +/// Helper function for alignment calculation. +#[inline(always)] +pub fn align(size: uint, align: uint) -> uint { + ((size + align) - 1u) & !(align - 1u) +} + +/// Adaptor to wrap around visitors implementing MovePtr. +pub struct MovePtrAdaptor<V> { + inner: V +} +pub fn MovePtrAdaptor<V:TyVisitor + MovePtr>(v: V) -> MovePtrAdaptor<V> { + MovePtrAdaptor { inner: v } +} + +pub impl<V:TyVisitor + MovePtr> MovePtrAdaptor<V> { + #[inline(always)] + fn bump(&self, sz: uint) { + do self.inner.move_ptr() |p| { + ((p as uint) + sz) as *c_void + }; + } + + #[inline(always)] + fn align(&self, a: uint) { + do self.inner.move_ptr() |p| { + align(p as uint, a) as *c_void + }; + } + + #[inline(always)] + fn align_to<T>(&self) { + self.align(sys::min_align_of::<T>()); + } + + #[inline(always)] + fn bump_past<T>(&self) { + self.bump(sys::size_of::<T>()); + } +} + +/// Abstract type-directed pointer-movement using the MovePtr trait +impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> { + fn visit_bot(&self) -> bool { + self.align_to::<()>(); + if ! self.inner.visit_bot() { return false; } + self.bump_past::<()>(); + true + } + + fn visit_nil(&self) -> bool { + self.align_to::<()>(); + if ! self.inner.visit_nil() { return false; } + self.bump_past::<()>(); + true + } + + fn visit_bool(&self) -> bool { + self.align_to::<bool>(); + if ! self.inner.visit_bool() { return false; } + self.bump_past::<bool>(); + true + } + + fn visit_int(&self) -> bool { + self.align_to::<int>(); + if ! self.inner.visit_int() { return false; } + self.bump_past::<int>(); + true + } + + fn visit_i8(&self) -> bool { + self.align_to::<i8>(); + if ! self.inner.visit_i8() { return false; } + self.bump_past::<i8>(); + true + } + + fn visit_i16(&self) -> bool { + self.align_to::<i16>(); + if ! self.inner.visit_i16() { return false; } + self.bump_past::<i16>(); + true + } + + fn visit_i32(&self) -> bool { + self.align_to::<i32>(); + if ! self.inner.visit_i32() { return false; } + self.bump_past::<i32>(); + true + } + + fn visit_i64(&self) -> bool { + self.align_to::<i64>(); + if ! self.inner.visit_i64() { return false; } + self.bump_past::<i64>(); + true + } + + fn visit_uint(&self) -> bool { + self.align_to::<uint>(); + if ! self.inner.visit_uint() { return false; } + self.bump_past::<uint>(); + true + } + + fn visit_u8(&self) -> bool { + self.align_to::<u8>(); + if ! self.inner.visit_u8() { return false; } + self.bump_past::<u8>(); + true + } + + fn visit_u16(&self) -> bool { + self.align_to::<u16>(); + if ! self.inner.visit_u16() { return false; } + self.bump_past::<u16>(); + true + } + + fn visit_u32(&self) -> bool { + self.align_to::<u32>(); + if ! self.inner.visit_u32() { return false; } + self.bump_past::<u32>(); + true + } + + fn visit_u64(&self) -> bool { + self.align_to::<u64>(); + if ! self.inner.visit_u64() { return false; } + self.bump_past::<u64>(); + true + } + + fn visit_float(&self) -> bool { + self.align_to::<float>(); + if ! self.inner.visit_float() { return false; } + self.bump_past::<float>(); + true + } + + fn visit_f32(&self) -> bool { + self.align_to::<f32>(); + if ! self.inner.visit_f32() { return false; } + self.bump_past::<f32>(); + true + } + + fn visit_f64(&self) -> bool { + self.align_to::<f64>(); + if ! self.inner.visit_f64() { return false; } + self.bump_past::<f64>(); + true + } + + fn visit_char(&self) -> bool { + self.align_to::<char>(); + if ! self.inner.visit_char() { return false; } + self.bump_past::<char>(); + true + } + + fn visit_str(&self) -> bool { + self.align_to::<~str>(); + if ! self.inner.visit_str() { return false; } + self.bump_past::<~str>(); + true + } + + fn visit_estr_box(&self) -> bool { + self.align_to::<@str>(); + if ! self.inner.visit_estr_box() { return false; } + self.bump_past::<@str>(); + true + } + + fn visit_estr_uniq(&self) -> bool { + self.align_to::<~str>(); + if ! self.inner.visit_estr_uniq() { return false; } + self.bump_past::<~str>(); + true + } + + fn visit_estr_slice(&self) -> bool { + self.align_to::<&'static str>(); + if ! self.inner.visit_estr_slice() { return false; } + self.bump_past::<&'static str>(); + true + } + + fn visit_estr_fixed(&self, n: uint, + sz: uint, + align: uint) -> bool { + self.align(align); + if ! self.inner.visit_estr_fixed(n, sz, align) { return false; } + self.bump(sz); + true + } + + fn visit_box(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<@u8>(); + if ! self.inner.visit_box(mtbl, inner) { return false; } + self.bump_past::<@u8>(); + true + } + + fn visit_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<~u8>(); + if ! self.inner.visit_uniq(mtbl, inner) { return false; } + self.bump_past::<~u8>(); + true + } + + fn visit_ptr(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<*u8>(); + if ! self.inner.visit_ptr(mtbl, inner) { return false; } + self.bump_past::<*u8>(); + true + } + + fn visit_rptr(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<&'static u8>(); + if ! self.inner.visit_rptr(mtbl, inner) { return false; } + self.bump_past::<&'static u8>(); + true + } + + fn visit_unboxed_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<vec::UnboxedVecRepr>(); + if ! self.inner.visit_vec(mtbl, inner) { return false; } + true + } + + fn visit_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<~[u8]>(); + if ! self.inner.visit_vec(mtbl, inner) { return false; } + self.bump_past::<~[u8]>(); + true + } + + fn visit_evec_box(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<@[u8]>(); + if ! self.inner.visit_evec_box(mtbl, inner) { return false; } + self.bump_past::<@[u8]>(); + true + } + + fn visit_evec_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<~[u8]>(); + if ! self.inner.visit_evec_uniq(mtbl, inner) { return false; } + self.bump_past::<~[u8]>(); + true + } + + fn visit_evec_slice(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.align_to::<&'static [u8]>(); + if ! self.inner.visit_evec_slice(mtbl, inner) { return false; } + self.bump_past::<&'static [u8]>(); + true + } + + fn visit_evec_fixed(&self, n: uint, sz: uint, align: uint, + mtbl: uint, inner: *TyDesc) -> bool { + self.align(align); + if ! self.inner.visit_evec_fixed(n, sz, align, mtbl, inner) { + return false; + } + self.bump(sz); + true + } + + fn visit_enter_rec(&self, n_fields: uint, sz: uint, align: uint) -> bool { + self.align(align); + if ! self.inner.visit_enter_rec(n_fields, sz, align) { return false; } + true + } + + fn visit_rec_field(&self, i: uint, name: &str, + mtbl: uint, inner: *TyDesc) -> bool { + unsafe { self.align((*inner).align); } + if ! self.inner.visit_rec_field(i, name, mtbl, inner) { + return false; + } + unsafe { self.bump((*inner).size); } + true + } + + fn visit_leave_rec(&self, n_fields: uint, sz: uint, align: uint) -> bool { + if ! self.inner.visit_leave_rec(n_fields, sz, align) { return false; } + true + } + + fn visit_enter_class(&self, n_fields: uint, sz: uint, align: uint) + -> bool { + self.align(align); + if ! self.inner.visit_enter_class(n_fields, sz, align) { + return false; + } + true + } + + fn visit_class_field(&self, i: uint, name: &str, + mtbl: uint, inner: *TyDesc) -> bool { + unsafe { self.align((*inner).align); } + if ! self.inner.visit_class_field(i, name, mtbl, inner) { + return false; + } + unsafe { self.bump((*inner).size); } + true + } + + fn visit_leave_class(&self, n_fields: uint, sz: uint, align: uint) + -> bool { + if ! self.inner.visit_leave_class(n_fields, sz, align) { + return false; + } + true + } + + fn visit_enter_tup(&self, n_fields: uint, sz: uint, align: uint) -> bool { + self.align(align); + if ! self.inner.visit_enter_tup(n_fields, sz, align) { return false; } + true + } + + fn visit_tup_field(&self, i: uint, inner: *TyDesc) -> bool { + unsafe { self.align((*inner).align); } + if ! self.inner.visit_tup_field(i, inner) { return false; } + unsafe { self.bump((*inner).size); } + true + } + + fn visit_leave_tup(&self, n_fields: uint, sz: uint, align: uint) -> bool { + if ! self.inner.visit_leave_tup(n_fields, sz, align) { return false; } + true + } + + fn visit_enter_fn(&self, purity: uint, proto: uint, + n_inputs: uint, retstyle: uint) -> bool { + if ! self.inner.visit_enter_fn(purity, proto, n_inputs, retstyle) { + return false + } + true + } + + fn visit_fn_input(&self, i: uint, mode: uint, inner: *TyDesc) -> bool { + if ! self.inner.visit_fn_input(i, mode, inner) { return false; } + true + } + + fn visit_fn_output(&self, retstyle: uint, inner: *TyDesc) -> bool { + if ! self.inner.visit_fn_output(retstyle, inner) { return false; } + true + } + + fn visit_leave_fn(&self, purity: uint, proto: uint, + n_inputs: uint, retstyle: uint) -> bool { + if ! self.inner.visit_leave_fn(purity, proto, n_inputs, retstyle) { + return false; + } + true + } + + fn visit_enter_enum(&self, n_variants: uint, + get_disr: extern unsafe fn(ptr: *Opaque) -> int, + sz: uint, align: uint) + -> bool { + self.align(align); + if ! self.inner.visit_enter_enum(n_variants, get_disr, sz, align) { + return false; + } + true + } + + fn visit_enter_enum_variant(&self, variant: uint, + disr_val: int, + n_fields: uint, + name: &str) -> bool { + if ! self.inner.visit_enter_enum_variant(variant, disr_val, + n_fields, name) { + return false; + } + true + } + + fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool { + self.inner.push_ptr(); + self.bump(offset); + if ! self.inner.visit_enum_variant_field(i, offset, inner) { return false; } + self.inner.pop_ptr(); + true + } + + fn visit_leave_enum_variant(&self, variant: uint, + disr_val: int, + n_fields: uint, + name: &str) -> bool { + if ! self.inner.visit_leave_enum_variant(variant, disr_val, + n_fields, name) { + return false; + } + true + } + + fn visit_leave_enum(&self, n_variants: uint, + get_disr: extern unsafe fn(ptr: *Opaque) -> int, + sz: uint, align: uint) -> bool { + if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) { + return false; + } + self.bump(sz); + true + } + + fn visit_trait(&self) -> bool { + self.align_to::<@TyVisitor>(); + if ! self.inner.visit_trait() { return false; } + self.bump_past::<@TyVisitor>(); + true + } + + fn visit_var(&self) -> bool { + if ! self.inner.visit_var() { return false; } + true + } + + fn visit_var_integral(&self) -> bool { + if ! self.inner.visit_var_integral() { return false; } + true + } + + fn visit_param(&self, i: uint) -> bool { + if ! self.inner.visit_param(i) { return false; } + true + } + + fn visit_self(&self) -> bool { + self.align_to::<&'static u8>(); + if ! self.inner.visit_self() { return false; } + self.align_to::<&'static u8>(); + true + } + + fn visit_type(&self) -> bool { + if ! self.inner.visit_type() { return false; } + true + } + + fn visit_opaque_box(&self) -> bool { + self.align_to::<@u8>(); + if ! self.inner.visit_opaque_box() { return false; } + self.bump_past::<@u8>(); + true + } + + fn visit_constr(&self, inner: *TyDesc) -> bool { + if ! self.inner.visit_constr(inner) { return false; } + true + } + + fn visit_closure_ptr(&self, ck: uint) -> bool { + self.align_to::<@fn()>(); + if ! self.inner.visit_closure_ptr(ck) { return false; } + self.bump_past::<@fn()>(); + true + } +} diff --git a/src/libstd/repr.rs b/src/libstd/repr.rs new file mode 100644 index 00000000000..a05009e375c --- /dev/null +++ b/src/libstd/repr.rs @@ -0,0 +1,639 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +More runtime type reflection + +*/ + +use cast::transmute; +use char; +use intrinsic; +use intrinsic::{TyDesc, TyVisitor, visit_tydesc}; +use intrinsic::Opaque; +use io::{Writer, WriterUtil}; +use libc::c_void; +use managed; +use ptr; +use reflect; +use reflect::{MovePtr, align}; +use str::StrSlice; +use to_str::ToStr; +use vec::raw::{VecRepr, SliceRepr}; +use vec; +use vec::{OwnedVector, UnboxedVecRepr}; + +#[cfg(test)] use io; + +pub use managed::raw::BoxRepr; + +/// Helpers + +trait EscapedCharWriter { + fn write_escaped_char(&self, ch: char); +} + +impl EscapedCharWriter for @Writer { + fn write_escaped_char(&self, ch: char) { + match ch { + '\t' => self.write_str("\\t"), + '\r' => self.write_str("\\r"), + '\n' => self.write_str("\\n"), + '\\' => self.write_str("\\\\"), + '\'' => self.write_str("\\'"), + '"' => self.write_str("\\\""), + '\x20'..'\x7e' => self.write_char(ch), + _ => { + // FIXME #4423: This is inefficient because it requires a + // malloc. + self.write_str(char::escape_unicode(ch)) + } + } + } +} + +/// Representations + +trait Repr { + fn write_repr(&self, writer: @Writer); +} + +impl Repr for () { + fn write_repr(&self, writer: @Writer) { writer.write_str("()"); } +} + +impl Repr for bool { + fn write_repr(&self, writer: @Writer) { + writer.write_str(if *self { "true" } else { "false" }) + } +} + +impl Repr for int { + fn write_repr(&self, writer: @Writer) { writer.write_int(*self); } +} +impl Repr for i8 { + fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } +} +impl Repr for i16 { + fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } +} +impl Repr for i32 { + fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } +} +impl Repr for i64 { + // FIXME #4424: This can lose precision. + fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } +} + +impl Repr for uint { + fn write_repr(&self, writer: @Writer) { writer.write_uint(*self); } +} +impl Repr for u8 { + fn write_repr(&self, writer: @Writer) { + writer.write_uint(*self as uint); + } +} +impl Repr for u16 { + fn write_repr(&self, writer: @Writer) { + writer.write_uint(*self as uint); + } +} +impl Repr for u32 { + fn write_repr(&self, writer: @Writer) { + writer.write_uint(*self as uint); + } +} +impl Repr for u64 { + // FIXME #4424: This can lose precision. + fn write_repr(&self, writer: @Writer) { + writer.write_uint(*self as uint); + } +} + +impl Repr for float { + // FIXME #4423: This mallocs. + fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } +} +impl Repr for f32 { + // FIXME #4423 This mallocs. + fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } +} +impl Repr for f64 { + // FIXME #4423: This mallocs. + fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } +} + +impl Repr for char { + fn write_repr(&self, writer: @Writer) { writer.write_char(*self); } +} + + +// New implementation using reflect::MovePtr + +enum VariantState { + SearchingFor(int), + Matched, + AlreadyFound +} + +pub struct ReprVisitor { + ptr: @mut *c_void, + ptr_stk: @mut ~[*c_void], + var_stk: @mut ~[VariantState], + writer: @Writer +} +pub fn ReprVisitor(ptr: *c_void, writer: @Writer) -> ReprVisitor { + ReprVisitor { + ptr: @mut ptr, + ptr_stk: @mut ~[], + var_stk: @mut ~[], + writer: writer, + } +} + +impl MovePtr for ReprVisitor { + #[inline(always)] + fn move_ptr(&self, adjustment: &fn(*c_void) -> *c_void) { + *self.ptr = adjustment(*self.ptr); + } + fn push_ptr(&self) { + self.ptr_stk.push(*self.ptr); + } + fn pop_ptr(&self) { + *self.ptr = self.ptr_stk.pop(); + } +} + +pub impl ReprVisitor { + + // Various helpers for the TyVisitor impl + + #[inline(always)] + fn get<T>(&self, f: &fn(&T)) -> bool { + unsafe { + f(transmute::<*c_void,&T>(*self.ptr)); + } + true + } + + #[inline(always)] + fn visit_inner(&self, inner: *TyDesc) -> bool { + self.visit_ptr_inner(*self.ptr, inner) + } + + #[inline(always)] + fn visit_ptr_inner(&self, ptr: *c_void, inner: *TyDesc) -> bool { + unsafe { + let u = ReprVisitor(ptr, self.writer); + let v = reflect::MovePtrAdaptor(u); + visit_tydesc(inner, @v as @TyVisitor); + true + } + } + + #[inline(always)] + fn write<T:Repr>(&self) -> bool { + do self.get |v:&T| { + v.write_repr(self.writer); + } + } + + fn write_escaped_slice(&self, slice: &str) { + self.writer.write_char('"'); + for slice.each_char |ch| { + self.writer.write_escaped_char(ch); + } + self.writer.write_char('"'); + } + + fn write_mut_qualifier(&self, mtbl: uint) { + if mtbl == 0 { + self.writer.write_str("mut "); + } else if mtbl == 1 { + // skip, this is ast::m_imm + } else { + assert_eq!(mtbl, 2); + self.writer.write_str("const "); + } + } + + fn write_vec_range(&self, mtbl: uint, ptr: *u8, len: uint, + inner: *TyDesc) -> bool { + let mut p = ptr; + let end = ptr::offset(p, len); + let (sz, al) = unsafe { ((*inner).size, (*inner).align) }; + self.writer.write_char('['); + let mut first = true; + while (p as uint) < (end as uint) { + if first { + first = false; + } else { + self.writer.write_str(", "); + } + self.write_mut_qualifier(mtbl); + self.visit_ptr_inner(p as *c_void, inner); + p = align(ptr::offset(p, sz) as uint, al) as *u8; + } + self.writer.write_char(']'); + true + } + + fn write_unboxed_vec_repr(&self, mtbl: uint, v: &UnboxedVecRepr, + inner: *TyDesc) -> bool { + self.write_vec_range(mtbl, ptr::to_unsafe_ptr(&v.data), + v.fill, inner) + } + + +} + +impl TyVisitor for ReprVisitor { + fn visit_bot(&self) -> bool { + self.writer.write_str("!"); + true + } + fn visit_nil(&self) -> bool { self.write::<()>() } + fn visit_bool(&self) -> bool { self.write::<bool>() } + fn visit_int(&self) -> bool { self.write::<int>() } + fn visit_i8(&self) -> bool { self.write::<i8>() } + fn visit_i16(&self) -> bool { self.write::<i16>() } + fn visit_i32(&self) -> bool { self.write::<i32>() } + fn visit_i64(&self) -> bool { self.write::<i64>() } + + fn visit_uint(&self) -> bool { self.write::<uint>() } + fn visit_u8(&self) -> bool { self.write::<u8>() } + fn visit_u16(&self) -> bool { self.write::<u16>() } + fn visit_u32(&self) -> bool { self.write::<u32>() } + fn visit_u64(&self) -> bool { self.write::<u64>() } + + fn visit_float(&self) -> bool { self.write::<float>() } + fn visit_f32(&self) -> bool { self.write::<f32>() } + fn visit_f64(&self) -> bool { self.write::<f64>() } + + fn visit_char(&self) -> bool { + do self.get::<char> |&ch| { + self.writer.write_char('\''); + self.writer.write_escaped_char(ch); + self.writer.write_char('\''); + } + } + + // Type no longer exists, vestigial function. + fn visit_str(&self) -> bool { fail!(); } + + fn visit_estr_box(&self) -> bool { + do self.get::<@str> |s| { + self.writer.write_char('@'); + self.write_escaped_slice(*s); + } + } + fn visit_estr_uniq(&self) -> bool { + do self.get::<~str> |s| { + self.writer.write_char('~'); + self.write_escaped_slice(*s); + } + } + fn visit_estr_slice(&self) -> bool { + do self.get::<&str> |s| { + self.write_escaped_slice(*s); + } + } + + // Type no longer exists, vestigial function. + fn visit_estr_fixed(&self, _n: uint, _sz: uint, + _align: uint) -> bool { fail!(); } + + fn visit_box(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.writer.write_char('@'); + self.write_mut_qualifier(mtbl); + do self.get::<&managed::raw::BoxRepr> |b| { + let p = ptr::to_unsafe_ptr(&b.data) as *c_void; + self.visit_ptr_inner(p, inner); + } + } + + fn visit_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.writer.write_char('~'); + self.write_mut_qualifier(mtbl); + do self.get::<&managed::raw::BoxRepr> |b| { + let p = ptr::to_unsafe_ptr(&b.data) as *c_void; + self.visit_ptr_inner(p, inner); + } + } + + fn visit_ptr(&self, _mtbl: uint, _inner: *TyDesc) -> bool { + do self.get::<*c_void> |p| { + self.writer.write_str(fmt!("(0x%x as *())", + *p as uint)); + } + } + + fn visit_rptr(&self, mtbl: uint, inner: *TyDesc) -> bool { + self.writer.write_char('&'); + self.write_mut_qualifier(mtbl); + do self.get::<*c_void> |p| { + self.visit_ptr_inner(*p, inner); + } + } + + // Type no longer exists, vestigial function. + fn visit_vec(&self, _mtbl: uint, _inner: *TyDesc) -> bool { fail!(); } + + + fn visit_unboxed_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { + do self.get::<vec::UnboxedVecRepr> |b| { + self.write_unboxed_vec_repr(mtbl, b, inner); + } + } + + fn visit_evec_box(&self, mtbl: uint, inner: *TyDesc) -> bool { + do self.get::<&VecRepr> |b| { + self.writer.write_char('@'); + self.write_unboxed_vec_repr(mtbl, &b.unboxed, inner); + } + } + + fn visit_evec_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { + do self.get::<&VecRepr> |b| { + self.writer.write_char('~'); + self.write_unboxed_vec_repr(mtbl, &b.unboxed, inner); + } + } + + fn visit_evec_slice(&self, mtbl: uint, inner: *TyDesc) -> bool { + do self.get::<SliceRepr> |s| { + self.writer.write_char('&'); + self.write_vec_range(mtbl, s.data, s.len, inner); + } + } + + fn visit_evec_fixed(&self, _n: uint, sz: uint, _align: uint, + mtbl: uint, inner: *TyDesc) -> bool { + do self.get::<u8> |b| { + self.write_vec_range(mtbl, ptr::to_unsafe_ptr(b), sz, inner); + } + } + + fn visit_enter_rec(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + self.writer.write_char('{'); + true + } + + fn visit_rec_field(&self, i: uint, name: &str, + mtbl: uint, inner: *TyDesc) -> bool { + if i != 0 { + self.writer.write_str(", "); + } + self.write_mut_qualifier(mtbl); + self.writer.write_str(name); + self.writer.write_str(": "); + self.visit_inner(inner); + true + } + + fn visit_leave_rec(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + self.writer.write_char('}'); + true + } + + fn visit_enter_class(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + self.writer.write_char('{'); + true + } + fn visit_class_field(&self, i: uint, name: &str, + mtbl: uint, inner: *TyDesc) -> bool { + if i != 0 { + self.writer.write_str(", "); + } + self.write_mut_qualifier(mtbl); + self.writer.write_str(name); + self.writer.write_str(": "); + self.visit_inner(inner); + true + } + fn visit_leave_class(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + self.writer.write_char('}'); + true + } + + fn visit_enter_tup(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + self.writer.write_char('('); + true + } + fn visit_tup_field(&self, i: uint, inner: *TyDesc) -> bool { + if i != 0 { + self.writer.write_str(", "); + } + self.visit_inner(inner); + true + } + fn visit_leave_tup(&self, _n_fields: uint, + _sz: uint, _align: uint) -> bool { + if _n_fields == 1 { + self.writer.write_char(','); + } + self.writer.write_char(')'); + true + } + + fn visit_enter_enum(&self, + _n_variants: uint, + get_disr: extern unsafe fn(ptr: *Opaque) -> int, + _sz: uint, + _align: uint) -> bool { + let var_stk: &mut ~[VariantState] = self.var_stk; + let disr = unsafe { + get_disr(transmute(*self.ptr)) + }; + var_stk.push(SearchingFor(disr)); + true + } + + fn visit_enter_enum_variant(&self, _variant: uint, + disr_val: int, + n_fields: uint, + name: &str) -> bool { + let mut write = false; + match self.var_stk.pop() { + SearchingFor(sought) => { + if disr_val == sought { + self.var_stk.push(Matched); + write = true; + } else { + self.var_stk.push(SearchingFor(sought)); + } + } + Matched | AlreadyFound => { + self.var_stk.push(AlreadyFound); + } + } + + if write { + self.writer.write_str(name); + if n_fields > 0 { + self.writer.write_char('('); + } + } + true + } + + fn visit_enum_variant_field(&self, + i: uint, + _offset: uint, + inner: *TyDesc) + -> bool { + match self.var_stk[vec::uniq_len(&const *self.var_stk) - 1] { + Matched => { + if i != 0 { + self.writer.write_str(", "); + } + if ! self.visit_inner(inner) { + return false; + } + } + _ => () + } + true + } + + fn visit_leave_enum_variant(&self, _variant: uint, + _disr_val: int, + n_fields: uint, + _name: &str) -> bool { + match self.var_stk[vec::uniq_len(&const *self.var_stk) - 1] { + Matched => { + if n_fields > 0 { + self.writer.write_char(')'); + } + } + _ => () + } + true + } + + fn visit_leave_enum(&self, + _n_variants: uint, + _get_disr: extern unsafe fn(ptr: *Opaque) -> int, + _sz: uint, + _align: uint) + -> bool { + let var_stk: &mut ~[VariantState] = self.var_stk; + match var_stk.pop() { + SearchingFor(*) => fail!("enum value matched no variant"), + _ => true + } + } + + fn visit_enter_fn(&self, _purity: uint, _proto: uint, + _n_inputs: uint, _retstyle: uint) -> bool { true } + fn visit_fn_input(&self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { + true + } + fn visit_fn_output(&self, _retstyle: uint, _inner: *TyDesc) -> bool { + true + } + fn visit_leave_fn(&self, _purity: uint, _proto: uint, + _n_inputs: uint, _retstyle: uint) -> bool { true } + + + fn visit_trait(&self) -> bool { true } + fn visit_var(&self) -> bool { true } + fn visit_var_integral(&self) -> bool { true } + fn visit_param(&self, _i: uint) -> bool { true } + fn visit_self(&self) -> bool { true } + fn visit_type(&self) -> bool { true } + + fn visit_opaque_box(&self) -> bool { + self.writer.write_char('@'); + do self.get::<&managed::raw::BoxRepr> |b| { + let p = ptr::to_unsafe_ptr(&b.data) as *c_void; + self.visit_ptr_inner(p, b.header.type_desc); + } + } + + // Type no longer exists, vestigial function. + fn visit_constr(&self, _inner: *TyDesc) -> bool { fail!(); } + + fn visit_closure_ptr(&self, _ck: uint) -> bool { true } +} + +pub fn write_repr<T>(writer: @Writer, object: &T) { + unsafe { + let ptr = ptr::to_unsafe_ptr(object) as *c_void; + let tydesc = intrinsic::get_tydesc::<T>(); + let u = ReprVisitor(ptr, writer); + let v = reflect::MovePtrAdaptor(u); + visit_tydesc(tydesc, @v as @TyVisitor) + } +} + +#[cfg(test)] +struct P {a: int, b: float} + +#[test] +fn test_repr() { + + fn exact_test<T>(t: &T, e:&str) { + let s : &str = io::with_str_writer(|w| write_repr(w, t)); + if s != e { + error!("expected '%s', got '%s'", + e, s); + } + assert_eq!(s, e); + } + + exact_test(&10, "10"); + exact_test(&true, "true"); + exact_test(&false, "false"); + exact_test(&1.234, "1.234"); + exact_test(&(&"hello"), "\"hello\""); + exact_test(&(@"hello"), "@\"hello\""); + exact_test(&(~"he\u10f3llo"), "~\"he\\u10f3llo\""); + + // FIXME #4210: the mut fields are a bit off here. + exact_test(&(@10), "@10"); + exact_test(&(@mut 10), "@10"); + exact_test(&(~10), "~10"); + exact_test(&(&10), "&10"); + let mut x = 10; + exact_test(&(&mut x), "&mut 10"); + + exact_test(&(1,), "(1,)"); + exact_test(&(@[1,2,3,4,5,6,7,8]), + "@[1, 2, 3, 4, 5, 6, 7, 8]"); + exact_test(&(@[1u8,2u8,3u8,4u8]), + "@[1, 2, 3, 4]"); + exact_test(&(@["hi", "there"]), + "@[\"hi\", \"there\"]"); + exact_test(&(~["hi", "there"]), + "~[\"hi\", \"there\"]"); + exact_test(&(&["hi", "there"]), + "&[\"hi\", \"there\"]"); + exact_test(&(P{a:10, b:1.234}), + "{a: 10, b: 1.234}"); + exact_test(&(@P{a:10, b:1.234}), + "@{a: 10, b: 1.234}"); + exact_test(&(~P{a:10, b:1.234}), + "~{a: 10, b: 1.234}"); + exact_test(&(10_u8, ~"hello"), + "(10, ~\"hello\")"); + exact_test(&(10_u16, ~"hello"), + "(10, ~\"hello\")"); + exact_test(&(10_u32, ~"hello"), + "(10, ~\"hello\")"); + exact_test(&(10_u64, ~"hello"), + "(10, ~\"hello\")"); +} diff --git a/src/libstd/result.rs b/src/libstd/result.rs new file mode 100644 index 00000000000..cda2fe13e37 --- /dev/null +++ b/src/libstd/result.rs @@ -0,0 +1,456 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! A type representing either success or failure + +// NB: transitionary, de-mode-ing. + +use cmp::Eq; +use either; +use either::Either; +use kinds::Copy; +use option::{None, Option, Some}; +use old_iter::BaseIter; +use vec; +use vec::OwnedVector; + +/// The result type +#[deriving(Clone, Eq)] +pub enum Result<T, U> { + /// Contains the successful result value + Ok(T), + /// Contains the error value + Err(U) +} + +/** + * Get the value out of a successful result + * + * # Failure + * + * If the result is an error + */ +#[inline(always)] +pub fn get<T:Copy,U>(res: &Result<T, U>) -> T { + match *res { + Ok(copy t) => t, + Err(ref the_err) => + fail!("get called on error result: %?", *the_err) + } +} + +/** + * Get a reference to the value out of a successful result + * + * # Failure + * + * If the result is an error + */ +#[inline(always)] +pub fn get_ref<'a, T, U>(res: &'a Result<T, U>) -> &'a T { + match *res { + Ok(ref t) => t, + Err(ref the_err) => + fail!("get_ref called on error result: %?", *the_err) + } +} + +/** + * Get the value out of an error result + * + * # Failure + * + * If the result is not an error + */ +#[inline(always)] +pub fn get_err<T, U: Copy>(res: &Result<T, U>) -> U { + match *res { + Err(copy u) => u, + Ok(_) => fail!("get_err called on ok result") + } +} + +/// Returns true if the result is `ok` +#[inline(always)] +pub fn is_ok<T, U>(res: &Result<T, U>) -> bool { + match *res { + Ok(_) => true, + Err(_) => false + } +} + +/// Returns true if the result is `err` +#[inline(always)] +pub fn is_err<T, U>(res: &Result<T, U>) -> bool { + !is_ok(res) +} + +/** + * Convert to the `either` type + * + * `ok` result variants are converted to `either::right` variants, `err` + * result variants are converted to `either::left`. + */ +#[inline(always)] +pub fn to_either<T:Copy,U:Copy>(res: &Result<U, T>) + -> Either<T, U> { + match *res { + Ok(copy res) => either::Right(res), + Err(copy fail_) => either::Left(fail_) + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `ok` then the value is extracted and passed to `op` whereupon + * `op`s result is returned. if `res` is `err` then it is immediately + * returned. This function can be used to compose the results of two + * functions. + * + * Example: + * + * let res = chain(read_file(file)) { |buf| + * ok(parse_bytes(buf)) + * } + */ +#[inline(always)] +pub fn chain<T, U, V>(res: Result<T, V>, op: &fn(T) + -> Result<U, V>) -> Result<U, V> { + match res { + Ok(t) => op(t), + Err(e) => Err(e) + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `err` then the value is extracted and passed to `op` + * whereupon `op`s result is returned. if `res` is `ok` then it is + * immediately returned. This function can be used to pass through a + * successful result while handling an error. + */ +#[inline(always)] +pub fn chain_err<T, U, V>( + res: Result<T, V>, + op: &fn(t: V) -> Result<T, U>) + -> Result<T, U> { + match res { + Ok(t) => Ok(t), + Err(v) => op(v) + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `ok` then the value is extracted and passed to `op` whereupon + * `op`s result is returned. if `res` is `err` then it is immediately + * returned. This function can be used to compose the results of two + * functions. + * + * Example: + * + * iter(read_file(file)) { |buf| + * print_buf(buf) + * } + */ +#[inline(always)] +pub fn iter<T, E>(res: &Result<T, E>, f: &fn(&T)) { + match *res { + Ok(ref t) => f(t), + Err(_) => () + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `err` then the value is extracted and passed to `op` whereupon + * `op`s result is returned. if `res` is `ok` then it is immediately returned. + * This function can be used to pass through a successful result while + * handling an error. + */ +#[inline(always)] +pub fn iter_err<T, E>(res: &Result<T, E>, f: &fn(&E)) { + match *res { + Ok(_) => (), + Err(ref e) => f(e) + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `ok` then the value is extracted and passed to `op` whereupon + * `op`s result is wrapped in `ok` and returned. if `res` is `err` then it is + * immediately returned. This function can be used to compose the results of + * two functions. + * + * Example: + * + * let res = map(read_file(file)) { |buf| + * parse_bytes(buf) + * } + */ +#[inline(always)] +pub fn map<T, E: Copy, U: Copy>(res: &Result<T, E>, op: &fn(&T) -> U) + -> Result<U, E> { + match *res { + Ok(ref t) => Ok(op(t)), + Err(copy e) => Err(e) + } +} + +/** + * Call a function based on a previous result + * + * If `res` is `err` then the value is extracted and passed to `op` whereupon + * `op`s result is wrapped in an `err` and returned. if `res` is `ok` then it + * is immediately returned. This function can be used to pass through a + * successful result while handling an error. + */ +#[inline(always)] +pub fn map_err<T:Copy,E,F:Copy>(res: &Result<T, E>, op: &fn(&E) -> F) + -> Result<T, F> { + match *res { + Ok(copy t) => Ok(t), + Err(ref e) => Err(op(e)) + } +} + +pub impl<T, E> Result<T, E> { + #[inline(always)] + fn get_ref<'a>(&'a self) -> &'a T { get_ref(self) } + + #[inline(always)] + fn is_ok(&self) -> bool { is_ok(self) } + + #[inline(always)] + fn is_err(&self) -> bool { is_err(self) } + + #[inline(always)] + fn iter(&self, f: &fn(&T)) { iter(self, f) } + + #[inline(always)] + fn iter_err(&self, f: &fn(&E)) { iter_err(self, f) } + + #[inline(always)] + fn unwrap(self) -> T { unwrap(self) } + + #[inline(always)] + fn unwrap_err(self) -> E { unwrap_err(self) } + + #[inline(always)] + fn chain<U>(self, op: &fn(T) -> Result<U,E>) -> Result<U,E> { + chain(self, op) + } + + #[inline(always)] + fn chain_err<F>(self, op: &fn(E) -> Result<T,F>) -> Result<T,F> { + chain_err(self, op) + } +} + +pub impl<T:Copy,E> Result<T, E> { + #[inline(always)] + fn get(&self) -> T { get(self) } + + #[inline(always)] + fn map_err<F:Copy>(&self, op: &fn(&E) -> F) -> Result<T,F> { + map_err(self, op) + } +} + +pub impl<T, E: Copy> Result<T, E> { + #[inline(always)] + fn get_err(&self) -> E { get_err(self) } + + #[inline(always)] + fn map<U:Copy>(&self, op: &fn(&T) -> U) -> Result<U,E> { + map(self, op) + } +} + +/** + * Maps each element in the vector `ts` using the operation `op`. Should an + * error occur, no further mappings are performed and the error is returned. + * Should no error occur, a vector containing the result of each map is + * returned. + * + * Here is an example which increments every integer in a vector, + * checking for overflow: + * + * fn inc_conditionally(x: uint) -> result<uint,str> { + * if x == uint::max_value { return err("overflow"); } + * else { return ok(x+1u); } + * } + * map(~[1u, 2u, 3u], inc_conditionally).chain {|incd| + * assert!(incd == ~[2u, 3u, 4u]); + * } + */ +#[inline(always)] +pub fn map_vec<T,U:Copy,V:Copy>( + ts: &[T], op: &fn(&T) -> Result<V,U>) -> Result<~[V],U> { + + let mut vs: ~[V] = vec::with_capacity(vec::len(ts)); + for ts.each |t| { + match op(t) { + Ok(copy v) => vs.push(v), + Err(copy u) => return Err(u) + } + } + return Ok(vs); +} + +#[inline(always)] +pub fn map_opt<T,U:Copy,V:Copy>( + o_t: &Option<T>, op: &fn(&T) -> Result<V,U>) -> Result<Option<V>,U> { + + match *o_t { + None => Ok(None), + Some(ref t) => match op(t) { + Ok(copy v) => Ok(Some(v)), + Err(copy e) => Err(e) + } + } +} + +/** + * Same as map, but it operates over two parallel vectors. + * + * A precondition is used here to ensure that the vectors are the same + * length. While we do not often use preconditions in the standard + * library, a precondition is used here because result::t is generally + * used in 'careful' code contexts where it is both appropriate and easy + * to accommodate an error like the vectors being of different lengths. + */ +#[inline(always)] +pub fn map_vec2<S,T,U:Copy,V:Copy>(ss: &[S], ts: &[T], + op: &fn(&S,&T) -> Result<V,U>) -> Result<~[V],U> { + + assert!(vec::same_length(ss, ts)); + let n = vec::len(ts); + let mut vs = vec::with_capacity(n); + let mut i = 0u; + while i < n { + match op(&ss[i],&ts[i]) { + Ok(copy v) => vs.push(v), + Err(copy u) => return Err(u) + } + i += 1u; + } + return Ok(vs); +} + +/** + * Applies op to the pairwise elements from `ss` and `ts`, aborting on + * error. This could be implemented using `map_zip()` but it is more efficient + * on its own as no result vector is built. + */ +#[inline(always)] +pub fn iter_vec2<S,T,U:Copy>(ss: &[S], ts: &[T], + op: &fn(&S,&T) -> Result<(),U>) -> Result<(),U> { + + assert!(vec::same_length(ss, ts)); + let n = vec::len(ts); + let mut i = 0u; + while i < n { + match op(&ss[i],&ts[i]) { + Ok(()) => (), + Err(copy u) => return Err(u) + } + i += 1u; + } + return Ok(()); +} + +/// Unwraps a result, assuming it is an `ok(T)` +#[inline(always)] +pub fn unwrap<T, U>(res: Result<T, U>) -> T { + match res { + Ok(t) => t, + Err(_) => fail!("unwrap called on an err result") + } +} + +/// Unwraps a result, assuming it is an `err(U)` +#[inline(always)] +pub fn unwrap_err<T, U>(res: Result<T, U>) -> U { + match res { + Err(u) => u, + Ok(_) => fail!("unwrap called on an ok result") + } +} + +#[cfg(test)] +#[allow(non_implicitly_copyable_typarams)] +mod tests { + use result::{Err, Ok, Result, chain, get, get_err}; + use result; + + pub fn op1() -> result::Result<int, ~str> { result::Ok(666) } + + pub fn op2(i: int) -> result::Result<uint, ~str> { + result::Ok(i as uint + 1u) + } + + pub fn op3() -> result::Result<int, ~str> { result::Err(~"sadface") } + + #[test] + pub fn chain_success() { + assert_eq!(get(&chain(op1(), op2)), 667u); + } + + #[test] + pub fn chain_failure() { + assert_eq!(get_err(&chain(op3(), op2)), ~"sadface"); + } + + #[test] + pub fn test_impl_iter() { + let mut valid = false; + Ok::<~str, ~str>(~"a").iter(|_x| valid = true); + assert!(valid); + + Err::<~str, ~str>(~"b").iter(|_x| valid = false); + assert!(valid); + } + + #[test] + pub fn test_impl_iter_err() { + let mut valid = true; + Ok::<~str, ~str>(~"a").iter_err(|_x| valid = false); + assert!(valid); + + valid = false; + Err::<~str, ~str>(~"b").iter_err(|_x| valid = true); + assert!(valid); + } + + #[test] + pub fn test_impl_map() { + assert_eq!(Ok::<~str, ~str>(~"a").map(|_x| ~"b"), Ok(~"b")); + assert_eq!(Err::<~str, ~str>(~"a").map(|_x| ~"b"), Err(~"a")); + } + + #[test] + pub fn test_impl_map_err() { + assert_eq!(Ok::<~str, ~str>(~"a").map_err(|_x| ~"b"), Ok(~"a")); + assert_eq!(Err::<~str, ~str>(~"a").map_err(|_x| ~"b"), Err(~"b")); + } + + #[test] + pub fn test_get_ref_method() { + let foo: Result<int, ()> = Ok(100); + assert_eq!(*foo.get_ref(), 100); + } +} diff --git a/src/libstd/rl.rs b/src/libstd/rl.rs deleted file mode 100644 index d15a8fc0136..00000000000 --- a/src/libstd/rl.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// FIXME #3921. This is unsafe because linenoise uses global mutable -// state without mutexes. - -use core::libc::{c_char, c_int}; - -pub mod rustrt { - use core::libc::{c_char, c_int}; - - pub extern { - pub unsafe fn linenoise(prompt: *c_char) -> *c_char; - pub unsafe fn linenoiseHistoryAdd(line: *c_char) -> c_int; - pub unsafe fn linenoiseHistorySetMaxLen(len: c_int) -> c_int; - pub unsafe fn linenoiseHistorySave(file: *c_char) -> c_int; - pub unsafe fn linenoiseHistoryLoad(file: *c_char) -> c_int; - pub unsafe fn linenoiseSetCompletionCallback(callback: *u8); - pub unsafe fn linenoiseAddCompletion(completions: *(), line: *c_char); - } -} - -/// Add a line to history -pub unsafe fn add_history(line: &str) -> bool { - do str::as_c_str(line) |buf| { - rustrt::linenoiseHistoryAdd(buf) == 1 as c_int - } -} - -/// Set the maximum amount of lines stored -pub unsafe fn set_history_max_len(len: int) -> bool { - rustrt::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int -} - -/// Save line history to a file -pub unsafe fn save_history(file: &str) -> bool { - do str::as_c_str(file) |buf| { - rustrt::linenoiseHistorySave(buf) == 1 as c_int - } -} - -/// Load line history from a file -pub unsafe fn load_history(file: &str) -> bool { - do str::as_c_str(file) |buf| { - rustrt::linenoiseHistoryLoad(buf) == 1 as c_int - } -} - -/// Print out a prompt and then wait for input and return it -pub unsafe fn read(prompt: &str) -> Option<~str> { - do str::as_c_str(prompt) |buf| { - let line = rustrt::linenoise(buf); - - if line.is_null() { None } - else { Some(str::raw::from_c_str(line)) } - } -} - -pub type CompletionCb<'self> = @fn(~str, &'self fn(~str)); - -fn complete_key(_v: @CompletionCb) {} - -/// Bind to the main completion callback -pub unsafe fn complete(cb: CompletionCb) { - local_data::local_data_set(complete_key, @(cb)); - - extern fn callback(line: *c_char, completions: *()) { - unsafe { - let cb = *local_data::local_data_get(complete_key) - .get(); - - do cb(str::raw::from_c_str(line)) |suggestion| { - do str::as_c_str(suggestion) |buf| { - rustrt::linenoiseAddCompletion(completions, buf); - } - } - } - } - - rustrt::linenoiseSetCompletionCallback(callback); -} diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs deleted file mode 100644 index 04444789f82..00000000000 --- a/src/libstd/rope.rs +++ /dev/null @@ -1,1430 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * High-level text containers. - * - * Ropes are a high-level representation of text that offers - * much better performance than strings for common operations, - * and generally reduce memory allocations and copies, while only - * entailing a small degradation of less common operations. - * - * More precisely, where a string is represented as a memory buffer, - * a rope is a tree structure whose leaves are slices of immutable - * strings. Therefore, concatenation, appending, prepending, substrings, - * etc. are operations that require only trivial tree manipulation, - * generally without having to copy memory. In addition, the tree - * structure of ropes makes them suitable as a form of index to speed-up - * access to Unicode characters by index in long chunks of text. - * - * The following operations are algorithmically faster in ropes: - * - * * extracting a subrope is logarithmic (linear in strings); - * * appending/prepending is near-constant time (linear in strings); - * * concatenation is near-constant time (linear in strings); - * * char length is constant-time (linear in strings); - * * access to a character by index is logarithmic (linear in strings); - */ - -/// The type of ropes. -pub type Rope = node::Root; - -/* - Section: Creating a rope - */ - -/// Create an empty rope -pub fn empty() -> Rope { - return node::Empty; -} - -/** - * Adopt a string as a rope. - * - * # Arguments - * - * * str - A valid string. - * - * # Return value - * - * A rope representing the same string as `str`. Depending of the length - * of `str`, this rope may be empty, flat or complex. - * - * # Performance notes - * - * * this operation does not copy the string; - * * the function runs in linear time. - */ -pub fn of_str(str: @~str) -> Rope { - return of_substr(str, 0u, str::len(*str)); -} - -/** - * As `of_str` but for a substring. - * - * # Arguments - * * byte_offset - The offset of `str` at which the rope starts. - * * byte_len - The number of bytes of `str` to use. - * - * # Return value - * - * A rope representing the same string as `str::substr(str, byte_offset, - * byte_len)`. Depending on `byte_len`, this rope may be empty, flat or - * complex. - * - * # Performance note - * - * This operation does not copy the substring. - * - * # Safety notes - * - * * this function does _not_ check the validity of the substring; - * * this function fails if `byte_offset` or `byte_len` do not match `str`. - */ -pub fn of_substr(str: @~str, byte_offset: uint, byte_len: uint) -> Rope { - if byte_len == 0u { return node::Empty; } - if byte_offset + byte_len > str::len(*str) { fail!(); } - return node::Content(node::of_substr(str, byte_offset, byte_len)); -} - -/* -Section: Adding things to a rope - */ - -/** - * Add one char to the end of the rope - * - * # Performance note - * - * * this function executes in near-constant time - */ -pub fn append_char(rope: Rope, char: char) -> Rope { - return append_str(rope, @str::from_chars([char])); -} - -/** - * Add one string to the end of the rope - * - * # Performance note - * - * * this function executes in near-linear time - */ -pub fn append_str(rope: Rope, str: @~str) -> Rope { - return append_rope(rope, of_str(str)) -} - -/** - * Add one char to the beginning of the rope - * - * # Performance note - * * this function executes in near-constant time - */ -pub fn prepend_char(rope: Rope, char: char) -> Rope { - return prepend_str(rope, @str::from_chars([char])); -} - -/** - * Add one string to the beginning of the rope - * - * # Performance note - * * this function executes in near-linear time - */ -pub fn prepend_str(rope: Rope, str: @~str) -> Rope { - return append_rope(of_str(str), rope) -} - -/// Concatenate two ropes -pub fn append_rope(left: Rope, right: Rope) -> Rope { - match (left) { - node::Empty => return right, - node::Content(left_content) => { - match (right) { - node::Empty => return left, - node::Content(right_content) => { - return node::Content(node::concat2(left_content, right_content)); - } - } - } - } -} - -/** - * Concatenate many ropes. - * - * If the ropes are balanced initially and have the same height, the resulting - * rope remains balanced. However, this function does not take any further - * measure to ensure that the result is balanced. - */ -pub fn concat(v: ~[Rope]) -> Rope { - //Copy `v` into a mut vector - let mut len = v.len(); - if len == 0u { return node::Empty; } - let mut ropes = vec::from_elem(len, v[0]); - for uint::range(1u, len) |i| { - ropes[i] = v[i]; - } - - //Merge progresively - while len > 1u { - for uint::range(0u, len/2u) |i| { - ropes[i] = append_rope(ropes[2u*i], ropes[2u*i+1u]); - } - if len%2u != 0u { - ropes[len/2u] = ropes[len - 1u]; - len = len/2u + 1u; - } else { - len = len/2u; - } - } - - //Return final rope - return ropes[0]; -} - - -/* -Section: Keeping ropes healthy - */ - - -/** - * Balance a rope. - * - * # Return value - * - * A copy of the rope in which small nodes have been grouped in memory, - * and with a reduced height. - * - * If you perform numerous rope concatenations, it is generally a good idea - * to rebalance your rope at some point, before using it for other purposes. - */ -pub fn bal(rope:Rope) -> Rope { - match (rope) { - node::Empty => return rope, - node::Content(x) => match (node::bal(x)) { - None => rope, - Some(y) => node::Content(y) - } - } -} - -/* -Section: Transforming ropes - */ - - -/** - * Extract a subrope from a rope. - * - * # Performance note - * - * * on a balanced rope, this operation takes algorithmic time; - * * this operation does not involve any copying - * - * # Safety note - * - * * this function fails if char_offset/char_len do not represent - * valid positions in rope - */ -pub fn sub_chars(rope: Rope, char_offset: uint, char_len: uint) -> Rope { - if char_len == 0u { return node::Empty; } - match (rope) { - node::Empty => fail!(), - node::Content(node) => if char_len > node::char_len(node) { - fail!() - } else { - return node::Content(node::sub_chars(node, char_offset, char_len)) - } - } -} - -/** - * Extract a subrope from a rope. - * - * # Performance note - * - * * on a balanced rope, this operation takes algorithmic time; - * * this operation does not involve any copying - * - * # Safety note - * - * * this function fails if byte_offset/byte_len do not represent - * valid positions in rope - */ -pub fn sub_bytes(rope: Rope, byte_offset: uint, byte_len: uint) -> Rope { - if byte_len == 0u { return node::Empty; } - match (rope) { - node::Empty => fail!(), - node::Content(node) =>if byte_len > node::byte_len(node) { - fail!() - } else { - return node::Content(node::sub_bytes(node, byte_offset, byte_len)) - } - } -} - -/* -Section: Comparing ropes - */ - -/** - * Compare two ropes by Unicode lexicographical order. - * - * This function compares only the contents of the rope, not their structure. - * - * # Return value - * - * A negative value if `left < right`, 0 if eq(left, right) or a positive - * value if `left > right` - */ -pub fn cmp(left: Rope, right: Rope) -> int { - match ((left, right)) { - (node::Empty, node::Empty) => return 0, - (node::Empty, _) => return -1, - (_, node::Empty) => return 1, - (node::Content(a), node::Content(b)) => { - return node::cmp(a, b); - } - } -} - -/** - * Returns `true` if both ropes have the same content (regardless of - * their structure), `false` otherwise - */ -pub fn eq(left: Rope, right: Rope) -> bool { - return cmp(left, right) == 0; -} - -/** - * # Arguments - * - * * left - an arbitrary rope - * * right - an arbitrary rope - * - * # Return value - * - * `true` if `left <= right` in lexicographical order (regardless of their - * structure), `false` otherwise - */ -pub fn le(left: Rope, right: Rope) -> bool { - return cmp(left, right) <= 0; -} - -/** - * # Arguments - * - * * left - an arbitrary rope - * * right - an arbitrary rope - * - * # Return value - * - * `true` if `left < right` in lexicographical order (regardless of their - * structure), `false` otherwise - */ -pub fn lt(left: Rope, right: Rope) -> bool { - return cmp(left, right) < 0; -} - -/** - * # Arguments - * - * * left - an arbitrary rope - * * right - an arbitrary rope - * - * # Return value - * - * `true` if `left >= right` in lexicographical order (regardless of their - * structure), `false` otherwise - */ -pub fn ge(left: Rope, right: Rope) -> bool { - return cmp(left, right) >= 0; -} - -/** - * # Arguments - * - * * left - an arbitrary rope - * * right - an arbitrary rope - * - * # Return value - * - * `true` if `left > right` in lexicographical order (regardless of their - * structure), `false` otherwise - */ -pub fn gt(left: Rope, right: Rope) -> bool { - return cmp(left, right) > 0; -} - -/* -Section: Iterating - */ - -/** - * Loop through a rope, char by char - * - * While other mechanisms are available, this is generally the best manner - * of looping through the contents of a rope char by char. If you prefer a - * loop that iterates through the contents string by string (e.g. to print - * the contents of the rope or output it to the system), however, - * you should rather use `traverse_components`. - * - * # Arguments - * - * * rope - A rope to traverse. It may be empty. - * * it - A block to execute with each consecutive character of the rope. - * Return `true` to continue, `false` to stop. - * - * # Return value - * - * `true` If execution proceeded correctly, `false` if it was interrupted, - * that is if `it` returned `false` at any point. - */ -pub fn loop_chars(rope: Rope, it: &fn(c: char) -> bool) -> bool { - match (rope) { - node::Empty => return true, - node::Content(x) => return node::loop_chars(x, it) - } -} - -/** - * Loop through a rope, char by char, until the end. - * - * # Arguments - * * rope - A rope to traverse. It may be empty - * * it - A block to execute with each consecutive character of the rope. - */ -pub fn iter_chars(rope: Rope, it: &fn(char)) { - do loop_chars(rope) |x| { - it(x); - true - }; -} - -/** - * Loop through a rope, string by string - * - * While other mechanisms are available, this is generally the best manner of - * looping through the contents of a rope string by string, which may be - * useful e.g. to print strings as you see them (without having to copy their - * contents into a new string), to send them to then network, to write them to - * a file, etc.. If you prefer a loop that iterates through the contents - * char by char (e.g. to search for a char), however, you should rather - * use `traverse`. - * - * # Arguments - * - * * rope - A rope to traverse. It may be empty - * * it - A block to execute with each consecutive string component of the - * rope. Return `true` to continue, `false` to stop - * - * # Return value - * - * `true` If execution proceeded correctly, `false` if it was interrupted, - * that is if `it` returned `false` at any point. - */ -pub fn loop_leaves(rope: Rope, it: &fn(node::Leaf) -> bool) -> bool{ - match (rope) { - node::Empty => return true, - node::Content(x) => return node::loop_leaves(x, it) - } -} - -pub mod iterator { - pub mod leaf { - use rope::{Rope, node}; - - - pub fn start(rope: Rope) -> node::leaf_iterator::T { - match (rope) { - node::Empty => return node::leaf_iterator::empty(), - node::Content(x) => return node::leaf_iterator::start(x) - } - } - pub fn next(it: &mut node::leaf_iterator::T) -> Option<node::Leaf> { - return node::leaf_iterator::next(it); - } - } - pub mod char { - use rope::{Rope, node}; - - - pub fn start(rope: Rope) -> node::char_iterator::T { - match (rope) { - node::Empty => return node::char_iterator::empty(), - node::Content(x) => return node::char_iterator::start(x) - } - } - pub fn next(it: &mut node::char_iterator::T) -> Option<char> { - return node::char_iterator::next(it) - } - } -} - -/* - Section: Rope properties - */ - -/** - * Returns the height of the rope. - * - * The height of the rope is a bound on the number of operations which - * must be performed during a character access before finding the leaf in - * which a character is contained. - * - * # Performance note - * - * Constant time. - */ -pub fn height(rope: Rope) -> uint { - match (rope) { - node::Empty => return 0u, - node::Content(x) => return node::height(x) - } -} - - - -/** - * The number of character in the rope - * - * # Performance note - * - * Constant time. - */ -pub fn char_len(rope: Rope) -> uint { - match (rope) { - node::Empty => return 0u, - node::Content(x) => return node::char_len(x) - } -} - -/** - * The number of bytes in the rope - * - * # Performance note - * - * Constant time. - */ -pub fn byte_len(rope: Rope) -> uint { - match (rope) { - node::Empty => return 0u, - node::Content(x) => return node::byte_len(x) - } -} - -/** - * The character at position `pos` - * - * # Arguments - * - * * pos - A position in the rope - * - * # Safety notes - * - * The function will fail if `pos` is not a valid position in the rope. - * - * # Performance note - * - * This function executes in a time proportional to the height of the - * rope + the (bounded) length of the largest leaf. - */ -pub fn char_at(rope: Rope, pos: uint) -> char { - match (rope) { - node::Empty => fail!(), - node::Content(x) => return node::char_at(x, pos) - } -} - - -/* - Section: Implementation -*/ -pub mod node { - use rope::node; - - /// Implementation of type `rope` - pub enum Root { - /// An empty rope - Empty, - /// A non-empty rope - Content(@Node), - } - - /** - * A text component in a rope. - * - * This is actually a slice in a rope, so as to ensure maximal sharing. - * - * # Fields - * - * * byte_offset = The number of bytes skipped in `content` - * * byte_len - The number of bytes of `content` to use - * * char_len - The number of chars in the leaf. - * * content - Contents of the leaf. - * - * Note that we can have `char_len < str::char_len(content)`, if - * this leaf is only a subset of the string. Also note that the - * string can be shared between several ropes, e.g. for indexing - * purposes. - */ - pub struct Leaf { - byte_offset: uint, - byte_len: uint, - char_len: uint, - content: @~str, - } - - /** - * A node obtained from the concatenation of two other nodes - * - * # Fields - * - * * left - The node containing the beginning of the text. - * * right - The node containing the end of the text. - * * char_len - The number of chars contained in all leaves of this node. - * * byte_len - The number of bytes in the subrope. - * - * Used to pre-allocate the correct amount of storage for - * serialization. - * - * * height - Height of the subrope. - * - * Used for rebalancing and to allocate stacks for traversals. - */ - pub struct Concat { - //FIXME (#2744): Perhaps a `vec` instead of `left`/`right` - left: @Node, - right: @Node, - char_len: uint, - byte_len: uint, - height: uint, - } - - pub enum Node { - /// A leaf consisting in a `str` - Leaf(Leaf), - /// The concatenation of two ropes - Concat(Concat), - } - - /** - * The maximal number of chars that _should_ be permitted in a single node - * - * This is not a strict value - */ - pub static hint_max_leaf_char_len: uint = 256u; - - /** - * The maximal height that _should_ be permitted in a tree. - * - * This is not a strict value - */ - pub static hint_max_node_height: uint = 16u; - - /** - * Adopt a string as a node. - * - * If the string is longer than `max_leaf_char_len`, it is - * logically split between as many leaves as necessary. Regardless, - * the string itself is not copied. - * - * Performance note: The complexity of this function is linear in - * the length of `str`. - */ - pub fn of_str(str: @~str) -> @Node { - return of_substr(str, 0u, str::len(*str)); - } - - /** - * Adopt a slice of a string as a node. - * - * If the slice is longer than `max_leaf_char_len`, it is logically split - * between as many leaves as necessary. Regardless, the string itself - * is not copied - * - * # Arguments - * - * * byte_start - The byte offset where the slice of `str` starts. - * * byte_len - The number of bytes from `str` to use. - * - * # Safety note - * - * Behavior is undefined if `byte_start` or `byte_len` do not represent - * valid positions in `str` - */ - pub fn of_substr(str: @~str, byte_start: uint, byte_len: uint) -> @Node { - return of_substr_unsafer(str, byte_start, byte_len, - str::count_chars(*str, byte_start, byte_len)); - } - - /** - * Adopt a slice of a string as a node. - * - * If the slice is longer than `max_leaf_char_len`, it is logically split - * between as many leaves as necessary. Regardless, the string itself - * is not copied - * - * # Arguments - * - * * byte_start - The byte offset where the slice of `str` starts. - * * byte_len - The number of bytes from `str` to use. - * * char_len - The number of chars in `str` in the interval - * [byte_start, byte_start+byte_len) - * - * # Safety notes - * - * * Behavior is undefined if `byte_start` or `byte_len` do not represent - * valid positions in `str` - * * Behavior is undefined if `char_len` does not accurately represent the - * number of chars between byte_start and byte_start+byte_len - */ - pub fn of_substr_unsafer(str: @~str, byte_start: uint, byte_len: uint, - char_len: uint) -> @Node { - assert!((byte_start + byte_len <= str::len(*str))); - let candidate = @Leaf(Leaf { - byte_offset: byte_start, - byte_len: byte_len, - char_len: char_len, - content: str, - }); - if char_len <= hint_max_leaf_char_len { - return candidate; - } else { - //Firstly, split `str` in slices of hint_max_leaf_char_len - let mut leaves = uint::div_ceil(char_len, hint_max_leaf_char_len); - //Number of leaves - let mut nodes = vec::from_elem(leaves, candidate); - - let mut i = 0u; - let mut offset = byte_start; - let first_leaf_char_len = - if char_len%hint_max_leaf_char_len == 0u { - hint_max_leaf_char_len - } else { - char_len%hint_max_leaf_char_len - }; - while i < leaves { - let chunk_char_len: uint = - if i == 0u { first_leaf_char_len } - else { hint_max_leaf_char_len }; - let chunk_byte_len = - str::count_bytes(*str, offset, chunk_char_len); - nodes[i] = @Leaf(Leaf { - byte_offset: offset, - byte_len: chunk_byte_len, - char_len: chunk_char_len, - content: str, - }); - - offset += chunk_byte_len; - i += 1u; - } - - //Then, build a tree from these slices by collapsing them - while leaves > 1u { - i = 0u; - while i < leaves - 1u {//Concat nodes 0 with 1, 2 with 3 etc. - nodes[i/2u] = concat2(nodes[i], nodes[i + 1u]); - i += 2u; - } - if i == leaves - 1u { - //And don't forget the last node if it is in even position - nodes[i/2u] = nodes[i]; - } - leaves = uint::div_ceil(leaves, 2u); - } - return nodes[0u]; - } - } - - pub fn byte_len(node: @Node) -> uint { - //FIXME (#2744): Could we do this without the pattern-matching? - match (*node) { - Leaf(y) => y.byte_len, - Concat(ref y) => y.byte_len - } - } - - pub fn char_len(node: @Node) -> uint { - match (*node) { - Leaf(y) => y.char_len, - Concat(ref y) => y.char_len - } - } - - /** - * Concatenate a forest of nodes into one tree. - * - * # Arguments - * - * * forest - The forest. This vector is progressively rewritten during - * execution and should be discarded as meaningless afterwards. - */ - pub fn tree_from_forest_destructive(forest: &mut [@Node]) -> @Node { - let mut i; - let mut len = forest.len(); - while len > 1u { - i = 0u; - while i < len - 1u {//Concat nodes 0 with 1, 2 with 3 etc. - let mut left = forest[i]; - let mut right = forest[i+1u]; - let left_len = char_len(left); - let right_len= char_len(right); - let mut left_height= height(left); - let mut right_height=height(right); - if left_len + right_len > hint_max_leaf_char_len { - if left_len <= hint_max_leaf_char_len { - left = flatten(left); - left_height = height(left); - } - if right_len <= hint_max_leaf_char_len { - right = flatten(right); - right_height = height(right); - } - } - if left_height >= hint_max_node_height { - left = of_substr_unsafer(@serialize_node(left), - 0u,byte_len(left), - left_len); - } - if right_height >= hint_max_node_height { - right = of_substr_unsafer(@serialize_node(right), - 0u,byte_len(right), - right_len); - } - forest[i/2u] = concat2(left, right); - i += 2u; - } - if i == len - 1u { - //And don't forget the last node if it is in even position - forest[i/2u] = forest[i]; - } - len = uint::div_ceil(len, 2u); - } - return forest[0]; - } - - pub fn serialize_node(node: @Node) -> ~str { - unsafe { - let mut buf = vec::from_elem(byte_len(node), 0); - let mut offset = 0u;//Current position in the buffer - let mut it = leaf_iterator::start(node); - loop { - match leaf_iterator::next(&mut it) { - None => break, - Some(x) => { - //FIXME (#2744): Replace with memcpy or something similar - let local_buf: ~[u8] = cast::transmute(copy *x.content); - let mut i = x.byte_offset; - while i < x.byte_len { - buf[offset] = local_buf[i]; - offset += 1u; - i += 1u; - } - cast::forget(local_buf); - } - } - } - return cast::transmute(buf); - } - } - - /** - * Replace a subtree by a single leaf with the same contents. - * - * * Performance note - * - * This function executes in linear time. - */ - pub fn flatten(node: @Node) -> @Node { - match (*node) { - Leaf(_) => node, - Concat(ref x) => { - @Leaf(Leaf { - byte_offset: 0u, - byte_len: x.byte_len, - char_len: x.char_len, - content: @serialize_node(node), - }) - } - } - } - - /** - * Balance a node. - * - * # Algorithm - * - * * if the node height is smaller than `hint_max_node_height`, do nothing - * * otherwise, gather all leaves as a forest, rebuild a balanced node, - * concatenating small leaves along the way - * - * # Return value - * - * * `None` if no transformation happened - * * `Some(x)` otherwise, in which case `x` has the same contents - * as `node` bot lower height and/or fragmentation. - */ - pub fn bal(node: @Node) -> Option<@Node> { - if height(node) < hint_max_node_height { return None; } - //1. Gather all leaves as a forest - let mut forest = ~[]; - let mut it = leaf_iterator::start(node); - loop { - match leaf_iterator::next(&mut it) { - None => break, - Some(x) => forest.push(@Leaf(x)) - } - } - //2. Rebuild tree from forest - let root = @*tree_from_forest_destructive(forest); - return Some(root); - - } - - /** - * Compute the subnode of a node. - * - * # Arguments - * - * * node - A node - * * byte_offset - A byte offset in `node` - * * byte_len - The number of bytes to return - * - * # Performance notes - * - * * this function performs no copying; - * * this function executes in a time proportional to the height of `node` - * - * # Safety notes - * - * This function fails if `byte_offset` or `byte_len` do not represent - * valid positions in `node`. - */ - pub fn sub_bytes(node: @Node, byte_offset: uint, - byte_len: uint) -> @Node { - let mut node = node; - let mut byte_offset = byte_offset; - loop { - if byte_offset == 0u && byte_len == node::byte_len(node) { - return node; - } - match (*node) { - node::Leaf(x) => { - let char_len = - str::count_chars(*x.content, byte_offset, byte_len); - return @Leaf(Leaf { - byte_offset: byte_offset, - byte_len: byte_len, - char_len: char_len, - content: x.content, - }); - } - node::Concat(ref x) => { - let left_len: uint = node::byte_len(x.left); - if byte_offset <= left_len { - if byte_offset + byte_len <= left_len { - //Case 1: Everything fits in x.left, tail-call - node = x.left; - } else { - //Case 2: A (non-empty, possibly full) suffix - //of x.left and a (non-empty, possibly full) prefix - //of x.right - let left_result = - sub_bytes(x.left, byte_offset, left_len); - let right_result = - sub_bytes(x.right, 0u, left_len - byte_offset); - return concat2(left_result, right_result); - } - } else { - //Case 3: Everything fits in x.right - byte_offset -= left_len; - node = x.right; - } - } - } - }; - } - - /** - * Compute the subnode of a node. - * - * # Arguments - * - * * node - A node - * * char_offset - A char offset in `node` - * * char_len - The number of chars to return - * - * # Performance notes - * - * * this function performs no copying; - * * this function executes in a time proportional to the height of `node` - * - * # Safety notes - * - * This function fails if `char_offset` or `char_len` do not represent - * valid positions in `node`. - */ - pub fn sub_chars(node: @Node, char_offset: uint, - char_len: uint) -> @Node { - let mut node = node; - let mut char_offset = char_offset; - loop { - match (*node) { - node::Leaf(x) => { - if char_offset == 0u && char_len == x.char_len { - return node; - } - let byte_offset = - str::count_bytes(*x.content, 0u, char_offset); - let byte_len = - str::count_bytes(*x.content, byte_offset, char_len); - return @Leaf(Leaf { - byte_offset: byte_offset, - byte_len: byte_len, - char_len: char_len, - content: x.content, - }); - } - node::Concat(ref x) => { - if char_offset == 0u && char_len == x.char_len {return node;} - let left_len : uint = node::char_len(x.left); - if char_offset <= left_len { - if char_offset + char_len <= left_len { - //Case 1: Everything fits in x.left, tail call - node = x.left; - } else { - //Case 2: A (non-empty, possibly full) suffix - //of x.left and a (non-empty, possibly full) prefix - //of x.right - let left_result = - sub_chars(x.left, char_offset, left_len); - let right_result = - sub_chars(x.right, 0u, left_len - char_offset); - return concat2(left_result, right_result); - } - } else { - //Case 3: Everything fits in x.right, tail call - node = x.right; - char_offset -= left_len; - } - } - } - }; - } - - pub fn concat2(left: @Node, right: @Node) -> @Node { - @Concat(Concat { - left: left, - right: right, - char_len: char_len(left) + char_len(right), - byte_len: byte_len(left) + byte_len(right), - height: uint::max(height(left), height(right)) + 1u, - }) - } - - pub fn height(node: @Node) -> uint { - match (*node) { - Leaf(_) => 0u, - Concat(ref x) => x.height, - } - } - - pub fn cmp(a: @Node, b: @Node) -> int { - let mut ita = char_iterator::start(a); - let mut itb = char_iterator::start(b); - let mut result = 0; - while result == 0 { - match (char_iterator::next(&mut ita), char_iterator::next(&mut itb)) - { - (None, None) => break, - (Some(chara), Some(charb)) => { - result = chara.cmp(&charb) as int; - } - (Some(_), _) => { - result = 1; - } - (_, Some(_)) => { - result = -1; - } - } - } - return result; - } - - pub fn loop_chars(node: @Node, it: &fn(c: char) -> bool) -> bool { - return loop_leaves(node,|leaf| { - str::all_between(*leaf.content, - leaf.byte_offset, - leaf.byte_len, it) - }); - } - - /** - * Loop through a node, leaf by leaf - * - * # Arguments - * - * * rope - A node to traverse. - * * it - A block to execute with each consecutive leaf of the node. - * Return `true` to continue, `false` to stop - * - * # Arguments - * - * `true` If execution proceeded correctly, `false` if it was interrupted, - * that is if `it` returned `false` at any point. - */ - pub fn loop_leaves(node: @Node, it: &fn(Leaf) -> bool) -> bool{ - let mut current = node; - loop { - match (*current) { - Leaf(x) => return it(x), - Concat(ref x) => if loop_leaves(x.left, it) { //non tail call - current = x.right; //tail call - } else { - return false; - } - } - }; - } - - /** - * # Arguments - * - * * pos - A position in the rope - * - * # Return value - * - * The character at position `pos` - * - * # Safety notes - * - * The function will fail if `pos` is not a valid position in the rope. - * - * Performance note: This function executes in a time - * proportional to the height of the rope + the (bounded) - * length of the largest leaf. - */ - pub fn char_at(mut node: @Node, mut pos: uint) -> char { - loop { - match *node { - Leaf(x) => return str::char_at(*x.content, pos), - Concat(Concat {left, right, _}) => { - let left_len = char_len(left); - node = if left_len > pos { left } - else { pos -= left_len; right }; - } - } - }; - } - - pub mod leaf_iterator { - use rope::node::{Concat, Leaf, Node, height}; - - pub struct T { - stack: ~[@Node], - stackpos: int, - } - - pub fn empty() -> T { - let stack : ~[@Node] = ~[]; - T { stack: stack, stackpos: -1 } - } - - pub fn start(node: @Node) -> T { - let stack = vec::from_elem(height(node)+1u, node); - T { - stack: stack, - stackpos: 0, - } - } - - pub fn next(it: &mut T) -> Option<Leaf> { - if it.stackpos < 0 { return None; } - loop { - let current = it.stack[it.stackpos]; - it.stackpos -= 1; - match (*current) { - Concat(ref x) => { - it.stackpos += 1; - it.stack[it.stackpos] = x.right; - it.stackpos += 1; - it.stack[it.stackpos] = x.left; - } - Leaf(x) => return Some(x) - } - }; - } - } - - pub mod char_iterator { - use rope::node::{Leaf, Node}; - use rope::node::leaf_iterator; - - pub struct T { - leaf_iterator: leaf_iterator::T, - leaf: Option<Leaf>, - leaf_byte_pos: uint, - } - - pub fn start(node: @Node) -> T { - T { - leaf_iterator: leaf_iterator::start(node), - leaf: None, - leaf_byte_pos: 0u, - } - } - - pub fn empty() -> T { - T { - leaf_iterator: leaf_iterator::empty(), - leaf: None, - leaf_byte_pos: 0u, - } - } - - pub fn next(it: &mut T) -> Option<char> { - loop { - match get_current_or_next_leaf(it) { - None => return None, - Some(_) => { - let next_char = get_next_char_in_leaf(it); - match next_char { - None => loop, - Some(_) => return next_char - } - } - } - }; - } - - pub fn get_current_or_next_leaf(it: &mut T) -> Option<Leaf> { - match it.leaf { - Some(_) => return it.leaf, - None => { - let next = leaf_iterator::next(&mut it.leaf_iterator); - match next { - None => return None, - Some(_) => { - it.leaf = next; - it.leaf_byte_pos = 0u; - return next; - } - } - } - } - } - - pub fn get_next_char_in_leaf(it: &mut T) -> Option<char> { - match copy it.leaf { - None => return None, - Some(aleaf) => { - if it.leaf_byte_pos >= aleaf.byte_len { - //We are actually past the end of the leaf - it.leaf = None; - return None - } else { - let range = - str::char_range_at(*aleaf.content, - (*it).leaf_byte_pos + aleaf.byte_offset); - let ch = range.ch; - let next = range.next; - (*it).leaf_byte_pos = next - aleaf.byte_offset; - return Some(ch) - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use rope::*; - use core::prelude::*; - - //Utility function, used for sanity check - fn rope_to_string(r: Rope) -> ~str { - match (r) { - node::Empty => return ~"", - node::Content(x) => { - let mut str = ~""; - fn aux(str: &mut ~str, node: @node::Node) { - match (*node) { - node::Leaf(x) => { - str::push_str( - str, - str::slice( - *x.content, x.byte_offset, - x.byte_offset + x.byte_len)); - } - node::Concat(ref x) => { - aux(str, x.left); - aux(str, x.right); - } - } - } - aux(&mut str, x); - return str - } - } - } - - - #[test] - fn trivial() { - assert_eq!(char_len(empty()), 0u); - assert_eq!(byte_len(empty()), 0u); - } - - #[test] - fn of_string1() { - let sample = @~"0123456789ABCDE"; - let r = of_str(sample); - - assert_eq!(char_len(r), str::char_len(*sample)); - assert!(rope_to_string(r) == *sample); - } - - #[test] - fn of_string2() { - let buf = @ mut ~"1234567890"; - let mut i = 0; - while i < 10 { - let a = copy *buf; - let b = copy *buf; - *buf = a + b; - i+=1; - } - let sample = @copy *buf; - let r = of_str(sample); - assert!(char_len(r) == str::char_len(*sample)); - assert!(rope_to_string(r) == *sample); - - let mut string_iter = 0u; - let string_len = str::len(*sample); - let mut rope_iter = iterator::char::start(r); - let mut equal = true; - while equal { - match (node::char_iterator::next(&mut rope_iter)) { - None => { - if string_iter < string_len { - equal = false; - } break; } - Some(c) => { - let range = str::char_range_at(*sample, string_iter); - string_iter = range.next; - if range.ch != c { equal = false; break; } - } - } - } - - assert!(equal); - } - - #[test] - fn iter1() { - let buf = @ mut ~"1234567890"; - let mut i = 0; - while i < 10 { - let a = copy *buf; - let b = copy *buf; - *buf = a + b; - i+=1; - } - let sample = @copy *buf; - let r = of_str(sample); - - let mut len = 0u; - let mut it = iterator::char::start(r); - loop { - match (node::char_iterator::next(&mut it)) { - None => break, - Some(_) => len += 1u - } - } - - assert_eq!(len, str::char_len(*sample)); - } - - #[test] - fn bal1() { - let init = @~"1234567890"; - let buf = @mut copy *init; - let mut i = 0; - while i < 8 { - let a = copy *buf; - let b = copy *buf; - *buf = a + b; - i+=1; - } - let sample = @copy *buf; - let r1 = of_str(sample); - let mut r2 = of_str(init); - i = 0; - while i < 8 { r2 = append_rope(r2, r2); i+= 1;} - - - assert!(eq(r1, r2)); - let r3 = bal(r2); - assert_eq!(char_len(r1), char_len(r3)); - - assert!(eq(r1, r3)); - } - - #[test] - #[ignore] - fn char_at1() { - //Generate a large rope - let mut r = of_str(@~"123456789"); - for uint::range(0u, 10u) |_i| { - r = append_rope(r, r); - } - - //Copy it in the slowest possible way - let mut r2 = empty(); - for uint::range(0u, char_len(r)) |i| { - r2 = append_char(r2, char_at(r, i)); - } - assert!(eq(r, r2)); - - let mut r3 = empty(); - for uint::range(0u, char_len(r)) |i| { - r3 = prepend_char(r3, char_at(r, char_len(r) - i - 1u)); - } - assert!(eq(r, r3)); - - //Additional sanity checks - let balr = bal(r); - let bal2 = bal(r2); - let bal3 = bal(r3); - assert!(eq(r, balr)); - assert!(eq(r, bal2)); - assert!(eq(r, bal3)); - assert!(eq(r2, r3)); - assert!(eq(bal2, bal3)); - } - - #[test] - fn concat1() { - //Generate a reasonable rope - let chunk = of_str(@~"123456789"); - let mut r = empty(); - for uint::range(0u, 10u) |_i| { - r = append_rope(r, chunk); - } - - //Same rope, obtained with rope::concat - let r2 = concat(vec::from_elem(10u, chunk)); - - assert!(eq(r, r2)); - } -} diff --git a/src/libstd/rt/context.rs b/src/libstd/rt/context.rs new file mode 100644 index 00000000000..0d011ce42ba --- /dev/null +++ b/src/libstd/rt/context.rs @@ -0,0 +1,214 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 option::*; +use super::stack::StackSegment; +use libc::c_void; +use cast::{transmute, transmute_mut_unsafe, + transmute_region, transmute_mut_region}; + +// XXX: Registers is boxed so that it is 16-byte aligned, for storing +// SSE regs. It would be marginally better not to do this. In C++ we +// use an attribute on a struct. +// XXX: It would be nice to define regs as `~Option<Registers>` since +// the registers are sometimes empty, but the discriminant would +// then misalign the regs again. +pub struct Context { + /// The context entry point, saved here for later destruction + start: Option<~~fn()>, + /// Hold the registers while the task or scheduler is suspended + regs: ~Registers +} + +pub impl Context { + fn empty() -> Context { + Context { + start: None, + regs: new_regs() + } + } + + /// Create a new context that will resume execution by running ~fn() + fn new(start: ~fn(), stack: &mut StackSegment) -> Context { + // XXX: Putting main into a ~ so it's a thin pointer and can + // be passed to the spawn function. Another unfortunate + // allocation + let start = ~start; + + // The C-ABI function that is the task entry point + extern fn task_start_wrapper(f: &~fn()) { (*f)() } + + let fp: *c_void = task_start_wrapper as *c_void; + let argp: *c_void = unsafe { transmute::<&~fn(), *c_void>(&*start) }; + let sp: *uint = stack.end(); + let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) }; + + // Save and then immediately load the current context, + // which we will then modify to call the given function when restored + let mut regs = new_regs(); + unsafe { + swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs)) + }; + + initialize_call_frame(&mut *regs, fp, argp, sp); + + return Context { + start: Some(start), + regs: regs + } + } + + /* Switch contexts + + Suspend the current execution context and resume another by + saving the registers values of the executing thread to a Context + then loading the registers from a previously saved Context. + */ + fn swap(out_context: &mut Context, in_context: &Context) { + let out_regs: &mut Registers = match out_context { + &Context { regs: ~ref mut r, _ } => r + }; + let in_regs: &Registers = match in_context { + &Context { regs: ~ref r, _ } => r + }; + + unsafe { swap_registers(out_regs, in_regs) }; + } +} + +extern { + #[rust_stack] + fn swap_registers(out_regs: *mut Registers, in_regs: *Registers); +} + +#[cfg(target_arch = "x86")] +struct Registers { + eax: u32, ebx: u32, ecx: u32, edx: u32, + ebp: u32, esi: u32, edi: u32, esp: u32, + cs: u16, ds: u16, ss: u16, es: u16, fs: u16, gs: u16, + eflags: u32, eip: u32 +} + +#[cfg(target_arch = "x86")] +fn new_regs() -> ~Registers { + ~Registers { + eax: 0, ebx: 0, ecx: 0, edx: 0, + ebp: 0, esi: 0, edi: 0, esp: 0, + cs: 0, ds: 0, ss: 0, es: 0, fs: 0, gs: 0, + eflags: 0, eip: 0 + } +} + +#[cfg(target_arch = "x86")] +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { + + let sp = align_down(sp); + let sp = mut_offset(sp, -4); + + unsafe { *sp = arg as uint }; + let sp = mut_offset(sp, -1); + unsafe { *sp = 0 }; // The final return address + + regs.esp = sp as u32; + regs.eip = fptr as u32; + + // Last base pointer on the stack is 0 + regs.ebp = 0; +} + +#[cfg(target_arch = "x86_64")] +type Registers = [uint, ..22]; + +#[cfg(target_arch = "x86_64")] +fn new_regs() -> ~Registers { ~([0, .. 22]) } + +#[cfg(target_arch = "x86_64")] +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { + + // Redefinitions from regs.h + static RUSTRT_ARG0: uint = 3; + static RUSTRT_RSP: uint = 1; + static RUSTRT_IP: uint = 8; + static RUSTRT_RBP: uint = 2; + + let sp = align_down(sp); + let sp = mut_offset(sp, -1); + + // The final return address. 0 indicates the bottom of the stack + unsafe { *sp = 0; } + + rtdebug!("creating call frame"); + rtdebug!("fptr %x", fptr as uint); + rtdebug!("arg %x", arg as uint); + rtdebug!("sp %x", sp as uint); + + regs[RUSTRT_ARG0] = arg as uint; + regs[RUSTRT_RSP] = sp as uint; + regs[RUSTRT_IP] = fptr as uint; + + // Last base pointer on the stack should be 0 + regs[RUSTRT_RBP] = 0; +} + +#[cfg(target_arch = "arm")] +type Registers = [uint, ..32]; + +#[cfg(target_arch = "arm")] +fn new_regs() -> ~Registers { ~([0, .. 32]) } + +#[cfg(target_arch = "arm")] +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { + let sp = align_down(sp); + // sp of arm eabi is 8-byte aligned + let sp = mut_offset(sp, -2); + + // The final return address. 0 indicates the bottom of the stack + unsafe { *sp = 0; } + + regs[0] = arg as uint; // r0 + regs[13] = sp as uint; // #53 sp, r13 + regs[14] = fptr as uint; // #60 pc, r15 --> lr +} + +#[cfg(target_arch = "mips")] +type Registers = [uint, ..32]; + +#[cfg(target_arch = "mips")] +fn new_regs() -> ~Registers { ~([0, .. 32]) } + +#[cfg(target_arch = "mips")] +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { + let sp = align_down(sp); + // sp of mips o32 is 8-byte aligned + let sp = mut_offset(sp, -2); + + // The final return address. 0 indicates the bottom of the stack + unsafe { *sp = 0; } + + regs[4] = arg as uint; + regs[29] = sp as uint; + regs[25] = fptr as uint; + regs[31] = fptr as uint; +} + +fn align_down(sp: *mut uint) -> *mut uint { + unsafe { + let sp: uint = transmute(sp); + let sp = sp & !(16 - 1); + transmute::<uint, *mut uint>(sp) + } +} + +// XXX: ptr::offset is positive ints only +#[inline(always)] +pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T { + use core::sys::size_of; + (ptr as int + count * (size_of::<T>() as int)) as *mut T +} diff --git a/src/libstd/rt/env.rs b/src/libstd/rt/env.rs new file mode 100644 index 00000000000..1d7ff173149 --- /dev/null +++ b/src/libstd/rt/env.rs @@ -0,0 +1,49 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Runtime environment settings + +use libc::{size_t, c_char, c_int}; + +pub struct Environment { + /// The number of threads to use by default + num_sched_threads: size_t, + /// The minimum size of a stack segment + min_stack_size: size_t, + /// The maximum amount of total stack per task before aborting + max_stack_size: size_t, + /// The default logging configuration + logspec: *c_char, + /// Record and report detailed information about memory leaks + detailed_leaks: bool, + /// Seed the random number generator + rust_seed: *c_char, + /// Poison allocations on free + poison_on_free: bool, + /// The argc value passed to main + argc: c_int, + /// The argv value passed to main + argv: **c_char, + /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set) + debug_mem: bool, + /// Print GC debugging info (true if env var RUST_DEBUG_BORROW is set) + debug_borrow: bool, +} + +/// Get the global environment settings +/// # Safety Note +/// This will abort the process if run outside of task context +pub fn get() -> &Environment { + unsafe { rust_get_rt_env() } +} + +extern { + fn rust_get_rt_env() -> &Environment; +} diff --git a/src/libstd/rt/io/comm_adapters.rs b/src/libstd/rt/io/comm_adapters.rs new file mode 100644 index 00000000000..7e891f1718e --- /dev/null +++ b/src/libstd/rt/io/comm_adapters.rs @@ -0,0 +1,58 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use super::{Reader, Writer}; + +struct PortReader<P>; + +impl<P: GenericPort<~[u8]>> PortReader<P> { + pub fn new(_port: P) -> PortReader<P> { fail!() } +} + +impl<P: GenericPort<~[u8]>> Reader for PortReader<P> { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +struct ChanWriter<C>; + +impl<C: GenericChan<~[u8]>> ChanWriter<C> { + pub fn new(_chan: C) -> ChanWriter<C> { fail!() } +} + +impl<C: GenericChan<~[u8]>> Writer for ChanWriter<C> { + pub fn write(&mut self, _buf: &[u8]) { fail!() } + + pub fn flush(&mut self) { fail!() } +} + +struct ReaderPort<R>; + +impl<R: Reader> ReaderPort<R> { + pub fn new(_reader: R) -> ReaderPort<R> { fail!() } +} + +impl<R: Reader> GenericPort<~[u8]> for ReaderPort<R> { + fn recv(&self) -> ~[u8] { fail!() } + + fn try_recv(&self) -> Option<~[u8]> { fail!() } +} + +struct WriterChan<W>; + +impl<W: Writer> WriterChan<W> { + pub fn new(_writer: W) -> WriterChan<W> { fail!() } +} + +impl<W: Writer> GenericChan<~[u8]> for WriterChan<W> { + fn send(&self, _x: ~[u8]) { fail!() } +} diff --git a/src/libstd/rt/io/extensions.rs b/src/libstd/rt/io/extensions.rs new file mode 100644 index 00000000000..ceff2ecd77d --- /dev/null +++ b/src/libstd/rt/io/extensions.rs @@ -0,0 +1,903 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Utility mixins that apply to all Readers and Writers + +// XXX: Not sure how this should be structured +// XXX: Iteration should probably be considered separately + +use uint; +use int; +use vec; +use rt::io::{Reader, Writer}; +use rt::io::{read_error, standard_error, EndOfFile, DEFAULT_BUF_SIZE}; +use option::{Option, Some, None}; +use unstable::finally::Finally; +use util; +use cast; +use io::{u64_to_le_bytes, u64_to_be_bytes}; + +pub trait ReaderUtil { + + /// Reads a single byte. Returns `None` on EOF. + /// + /// # Failure + /// + /// Raises the same conditions as the `read` method. Returns + /// `None` if the condition is handled. + fn read_byte(&mut self) -> Option<u8>; + + /// Reads `len` bytes and appends them to a vector. + /// + /// May push fewer than the requested number of bytes on error + /// or EOF. Returns true on success, false on EOF or error. + /// + /// # Failure + /// + /// Raises the same conditions as `read`. Additionally raises `read_error` + /// on EOF. If `read_error` is handled then `push_bytes` may push less + /// than the requested number of bytes. + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint); + + /// Reads `len` bytes and gives you back a new vector of length `len` + /// + /// # Failure + /// + /// Raises the same conditions as `read`. Additionally raises `read_error` + /// on EOF. If `read_error` is handled then the returned vector may + /// contain less than the requested number of bytes. + fn read_bytes(&mut self, len: uint) -> ~[u8]; + + /// Reads all remaining bytes from the stream. + /// + /// # Failure + /// + /// Raises the same conditions as the `read` method. + fn read_to_end(&mut self) -> ~[u8]; + +} + +pub trait ReaderByteConversions { + /// Reads `n` little-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_uint_n(&mut self, nbytes: uint) -> u64; + + /// Reads `n` little-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_int_n(&mut self, nbytes: uint) -> i64; + + /// Reads `n` big-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_uint_n(&mut self, nbytes: uint) -> u64; + + /// Reads `n` big-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_int_n(&mut self, nbytes: uint) -> i64; + + /// Reads a little-endian unsigned integer. + /// + /// The number of bytes returned is system-dependant. + fn read_le_uint(&mut self) -> uint; + + /// Reads a little-endian integer. + /// + /// The number of bytes returned is system-dependant. + fn read_le_int(&mut self) -> int; + + /// Reads a big-endian unsigned integer. + /// + /// The number of bytes returned is system-dependant. + fn read_be_uint(&mut self) -> uint; + + /// Reads a big-endian integer. + /// + /// The number of bytes returned is system-dependant. + fn read_be_int(&mut self) -> int; + + /// Reads a big-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_be_u64(&mut self) -> u64; + + /// Reads a big-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_be_u32(&mut self) -> u32; + + /// Reads a big-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_be_u16(&mut self) -> u16; + + /// Reads a big-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_be_i64(&mut self) -> i64; + + /// Reads a big-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_be_i32(&mut self) -> i32; + + /// Reads a big-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_be_i16(&mut self) -> i16; + + /// Reads a big-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_be_f64(&mut self) -> f64; + + /// Reads a big-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_be_f32(&mut self) -> f32; + + /// Reads a little-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_le_u64(&mut self) -> u64; + + /// Reads a little-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_le_u32(&mut self) -> u32; + + /// Reads a little-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_le_u16(&mut self) -> u16; + + /// Reads a little-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_le_i64(&mut self) -> i64; + + /// Reads a little-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_le_i32(&mut self) -> i32; + + /// Reads a little-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_le_i16(&mut self) -> i16; + + /// Reads a little-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_le_f64(&mut self) -> f64; + + /// Reads a little-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_le_f32(&mut self) -> f32; + + /// Read a u8. + /// + /// `u8`s are 1 byte. + fn read_u8(&mut self) -> u8; + + /// Read an i8. + /// + /// `i8`s are 1 byte. + fn read_i8(&mut self) -> i8; + +} + +pub trait WriterByteConversions { + /// Write the result of passing n through `int::to_str_bytes`. + fn write_int(&mut self, n: int); + + /// Write the result of passing n through `uint::to_str_bytes`. + fn write_uint(&mut self, n: uint); + + /// Write a little-endian uint (number of bytes depends on system). + fn write_le_uint(&mut self, n: uint); + + /// Write a little-endian int (number of bytes depends on system). + fn write_le_int(&mut self, n: int); + + /// Write a big-endian uint (number of bytes depends on system). + fn write_be_uint(&mut self, n: uint); + + /// Write a big-endian int (number of bytes depends on system). + fn write_be_int(&mut self, n: int); + + /// Write a big-endian u64 (8 bytes). + fn write_be_u64_(&mut self, n: u64); + + /// Write a big-endian u32 (4 bytes). + fn write_be_u32(&mut self, n: u32); + + /// Write a big-endian u16 (2 bytes). + fn write_be_u16(&mut self, n: u16); + + /// Write a big-endian i64 (8 bytes). + fn write_be_i64(&mut self, n: i64); + + /// Write a big-endian i32 (4 bytes). + fn write_be_i32(&mut self, n: i32); + + /// Write a big-endian i16 (2 bytes). + fn write_be_i16(&mut self, n: i16); + + /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + fn write_be_f64(&mut self, f: f64); + + /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + fn write_be_f32(&mut self, f: f32); + + /// Write a little-endian u64 (8 bytes). + fn write_le_u64_(&mut self, n: u64); + + /// Write a little-endian u32 (4 bytes). + fn write_le_u32(&mut self, n: u32); + + /// Write a little-endian u16 (2 bytes). + fn write_le_u16(&mut self, n: u16); + + /// Write a little-endian i64 (8 bytes). + fn write_le_i64(&mut self, n: i64); + + /// Write a little-endian i32 (4 bytes). + fn write_le_i32(&mut self, n: i32); + + /// Write a little-endian i16 (2 bytes). + fn write_le_i16(&mut self, n: i16); + + /// Write a little-endian IEEE754 double-precision floating-point + /// (8 bytes). + fn write_le_f64(&mut self, f: f64); + + /// Write a litten-endian IEEE754 single-precision floating-point + /// (4 bytes). + fn write_le_f32(&mut self, f: f32); + + /// Write a u8 (1 byte). + fn write_u8(&mut self, n: u8); + + /// Write a i8 (1 byte). + fn write_i8(&mut self, n: i8); +} + +impl<T: Reader> ReaderUtil for T { + fn read_byte(&mut self) -> Option<u8> { + let mut buf = [0]; + match self.read(buf) { + Some(0) => { + debug!("read 0 bytes. trying again"); + self.read_byte() + } + Some(1) => Some(buf[0]), + Some(_) => util::unreachable(), + None => None + } + } + + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) { + unsafe { + let start_len = buf.len(); + let mut total_read = 0; + + vec::reserve_at_least(buf, start_len + len); + vec::raw::set_len(buf, start_len + len); + + do (|| { + while total_read < len { + let slice = vec::mut_slice(*buf, start_len + total_read, buf.len()); + match self.read(slice) { + Some(nread) => { + total_read += nread; + } + None => { + read_error::cond.raise(standard_error(EndOfFile)); + break; + } + } + } + }).finally { + vec::raw::set_len(buf, start_len + total_read); + } + } + } + + fn read_bytes(&mut self, len: uint) -> ~[u8] { + let mut buf = vec::with_capacity(len); + self.push_bytes(&mut buf, len); + return buf; + } + + fn read_to_end(&mut self) -> ~[u8] { + let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE); + let mut keep_reading = true; + do read_error::cond.trap(|e| { + if e.kind == EndOfFile { + keep_reading = false; + } else { + read_error::cond.raise(e) + } + }).in { + while keep_reading { + self.push_bytes(&mut buf, DEFAULT_BUF_SIZE) + } + } + return buf; + } +} + +impl<T: Reader> ReaderByteConversions for T { + fn read_le_uint_n(&mut self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, pos = 0, i = nbytes; + while i > 0 { + val += (self.read_u8() as u64) << pos; + pos += 8; + i -= 1; + } + val + } + + fn read_le_int_n(&mut self, nbytes: uint) -> i64 { + extend_sign(self.read_le_uint_n(nbytes), nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, i = nbytes; + while i > 0 { + i -= 1; + val += (self.read_u8() as u64) << i * 8; + } + val + } + + fn read_be_int_n(&mut self, nbytes: uint) -> i64 { + extend_sign(self.read_be_uint_n(nbytes), nbytes) + } + + fn read_le_uint(&mut self) -> uint { + self.read_le_uint_n(uint::bytes) as uint + } + + fn read_le_int(&mut self) -> int { + self.read_le_int_n(int::bytes) as int + } + + fn read_be_uint(&mut self) -> uint { + self.read_be_uint_n(uint::bytes) as uint + } + + fn read_be_int(&mut self) -> int { + self.read_be_int_n(int::bytes) as int + } + + fn read_be_u64(&mut self) -> u64 { + self.read_be_uint_n(8) as u64 + } + + fn read_be_u32(&mut self) -> u32 { + self.read_be_uint_n(4) as u32 + } + + fn read_be_u16(&mut self) -> u16 { + self.read_be_uint_n(2) as u16 + } + + fn read_be_i64(&mut self) -> i64 { + self.read_be_int_n(8) as i64 + } + + fn read_be_i32(&mut self) -> i32 { + self.read_be_int_n(4) as i32 + } + + fn read_be_i16(&mut self) -> i16 { + self.read_be_int_n(2) as i16 + } + + fn read_be_f64(&mut self) -> f64 { + unsafe { + cast::transmute::<u64, f64>(self.read_be_u64()) + } + } + + fn read_be_f32(&mut self) -> f32 { + unsafe { + cast::transmute::<u32, f32>(self.read_be_u32()) + } + } + + fn read_le_u64(&mut self) -> u64 { + self.read_le_uint_n(8) as u64 + } + + fn read_le_u32(&mut self) -> u32 { + self.read_le_uint_n(4) as u32 + } + + fn read_le_u16(&mut self) -> u16 { + self.read_le_uint_n(2) as u16 + } + + fn read_le_i64(&mut self) -> i64 { + self.read_le_int_n(8) as i64 + } + + fn read_le_i32(&mut self) -> i32 { + self.read_le_int_n(4) as i32 + } + + fn read_le_i16(&mut self) -> i16 { + self.read_le_int_n(2) as i16 + } + + fn read_le_f64(&mut self) -> f64 { + unsafe { + cast::transmute::<u64, f64>(self.read_le_u64()) + } + } + + fn read_le_f32(&mut self) -> f32 { + unsafe { + cast::transmute::<u32, f32>(self.read_le_u32()) + } + } + + fn read_u8(&mut self) -> u8 { + match self.read_byte() { + Some(b) => b as u8, + None => 0 + } + } + + fn read_i8(&mut self) -> i8 { + match self.read_byte() { + Some(b) => b as i8, + None => 0 + } + } + +} + +impl<T: Writer> WriterByteConversions for T { + fn write_int(&mut self, n: int) { + int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + + fn write_uint(&mut self, n: uint) { + uint::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + + fn write_le_uint(&mut self, n: uint) { + u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + + fn write_le_int(&mut self, n: int) { + u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v)) + } + + fn write_be_uint(&mut self, n: uint) { + u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + + fn write_be_int(&mut self, n: int) { + u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v)) + } + + fn write_be_u64_(&mut self, n: u64) { + u64_to_be_bytes(n, 8u, |v| self.write(v)) + } + + fn write_be_u32(&mut self, n: u32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_be_u16(&mut self, n: u16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_be_i64(&mut self, n: i64) { + u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) + } + + fn write_be_i32(&mut self, n: i32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_be_i16(&mut self, n: i16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_be_f64(&mut self, f: f64) { + unsafe { + self.write_be_u64_(cast::transmute(f)) + } + } + + fn write_be_f32(&mut self, f: f32) { + unsafe { + self.write_be_u32(cast::transmute(f)) + } + } + + fn write_le_u64_(&mut self, n: u64) { + u64_to_le_bytes(n, 8u, |v| self.write(v)) + } + + fn write_le_u32(&mut self, n: u32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_le_u16(&mut self, n: u16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_le_i64(&mut self, n: i64) { + u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) + } + + fn write_le_i32(&mut self, n: i32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_le_i16(&mut self, n: i16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_le_f64(&mut self, f: f64) { + unsafe { + self.write_le_u64_(cast::transmute(f)) + } + } + + fn write_le_f32(&mut self, f: f32) { + unsafe { + self.write_le_u32(cast::transmute(f)) + } + } + + fn write_u8(&mut self, n: u8) { + self.write([n]) + } + + fn write_i8(&mut self, n: i8) { + self.write([n as u8]) + } +} + +fn extend_sign(val: u64, nbytes: uint) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + +#[cfg(test)] +mod test { + use super::{ReaderUtil, ReaderByteConversions, WriterByteConversions}; + use u64; + use i32; + use option::{Some, None}; + use cell::Cell; + use rt::io::mem::{MemReader, MemWriter}; + use rt::io::mock::MockReader; + use rt::io::{read_error, placeholder_error}; + + #[test] + fn read_byte() { + let mut reader = MemReader::new(~[10]); + let byte = reader.read_byte(); + assert!(byte == Some(10)); + } + + #[test] + fn read_byte_0_bytes() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + Some(0) + } else { + buf[0] = 10; + Some(1) + } + } + }; + let byte = reader.read_byte(); + assert!(byte == Some(10)); + } + + #[test] + fn read_byte_eof() { + let mut reader = MockReader::new(); + reader.read = |_| None; + let byte = reader.read_byte(); + assert!(byte == None); + } + + #[test] + fn read_byte_error() { + let mut reader = MockReader::new(); + reader.read = |_| { + read_error::cond.raise(placeholder_error()); + None + }; + do read_error::cond.trap(|_| { + }).in { + let byte = reader.read_byte(); + assert!(byte == None); + } + } + + #[test] + fn read_bytes() { + let mut reader = MemReader::new(~[10, 11, 12, 13]); + let bytes = reader.read_bytes(4); + assert!(bytes == ~[10, 11, 12, 13]); + } + + #[test] + fn read_bytes_partial() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + buf[0] = 12; + buf[1] = 13; + Some(2) + } + } + }; + let bytes = reader.read_bytes(4); + assert!(bytes == ~[10, 11, 12, 13]); + } + + #[test] + fn read_bytes_eof() { + let mut reader = MemReader::new(~[10, 11]); + do read_error::cond.trap(|_| { + }).in { + assert!(reader.read_bytes(4) == ~[10, 11]); + } + } + + #[test] + fn push_bytes() { + let mut reader = MemReader::new(~[10, 11, 12, 13]); + let mut buf = ~[8, 9]; + reader.push_bytes(&mut buf, 4); + assert!(buf == ~[8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_bytes_partial() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + buf[0] = 12; + buf[1] = 13; + Some(2) + } + } + }; + let mut buf = ~[8, 9]; + reader.push_bytes(&mut buf, 4); + assert!(buf == ~[8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_bytes_eof() { + let mut reader = MemReader::new(~[10, 11]); + let mut buf = ~[8, 9]; + do read_error::cond.trap(|_| { + }).in { + reader.push_bytes(&mut buf, 4); + assert!(buf == ~[8, 9, 10, 11]); + } + } + + #[test] + fn push_bytes_error() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + Some(1) + } else { + read_error::cond.raise(placeholder_error()); + None + } + } + }; + let mut buf = ~[8, 9]; + do read_error::cond.trap(|_| { } ).in { + reader.push_bytes(&mut buf, 4); + } + assert!(buf == ~[8, 9, 10]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn push_bytes_fail_reset_len() { + use unstable::finally::Finally; + + // push_bytes unsafely sets the vector length. This is testing that + // upon failure the length is reset correctly. + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + Some(1) + } else { + read_error::cond.raise(placeholder_error()); + None + } + } + }; + let buf = @mut ~[8, 9]; + do (|| { + reader.push_bytes(&mut *buf, 4); + }).finally { + // NB: Using rtassert here to trigger abort on failure since this is a should_fail test + rtassert!(*buf == ~[8, 9, 10]); + } + } + + #[test] + fn read_to_end() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else if *count == 1 { + *count = 2; + buf[0] = 12; + buf[1] = 13; + Some(2) + } else { + None + } + } + }; + let buf = reader.read_to_end(); + assert!(buf == ~[10, 11, 12, 13]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn read_to_end_error() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + read_error::cond.raise(placeholder_error()); + None + } + } + }; + let buf = reader.read_to_end(); + assert!(buf == ~[10, 11]); + } + + // XXX: Some problem with resolve here + /*#[test] + fn test_read_write_le() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + let mut writer = MemWriter::new(); + for uints.each |i| { + writer.write_le_u64(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for uints.each |i| { + assert!(reader.read_le_u64() == *i); + } + } + + #[test] + fn test_read_write_be() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + let mut writer = MemWriter::new(); + for uints.each |i| { + writer.write_be_u64(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for uints.each |i| { + assert!(reader.read_be_u64() == *i); + } + } + + #[test] + fn test_read_be_int_n() { + let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; + + let mut writer = MemWriter::new(); + for ints.each |i| { + writer.write_be_i32(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for ints.each |i| { + // this tests that the sign extension is working + // (comparing the values as i32 would not test this) + assert!(reader.read_be_int_n(4) == *i as i64); + } + } + + #[test] + fn test_read_f32() { + //big-endian floating-point 8.1250 + let buf = ~[0x41, 0x02, 0x00, 0x00]; + + let mut writer = MemWriter::new(); + writer.write(buf); + + let mut reader = MemReader::new(writer.inner()); + let f = reader.read_be_f32(); + assert!(f == 8.1250); + } + + #[test] + fn test_read_write_f32() { + let f:f32 = 8.1250; + + let mut writer = MemWriter::new(); + writer.write_be_f32(f); + writer.write_le_f32(f); + + let mut reader = MemReader::new(writer.inner()); + assert!(reader.read_be_f32() == 8.1250); + assert!(reader.read_le_f32() == 8.1250); + }*/ + +} diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs new file mode 100644 index 00000000000..1f61cf25fbd --- /dev/null +++ b/src/libstd/rt/io/file.rs @@ -0,0 +1,79 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use super::support::PathLike; +use super::{Reader, Writer, Seek}; +use super::SeekStyle; + +/// # XXX +/// * Ugh, this is ridiculous. What is the best way to represent these options? +enum FileMode { + /// Opens an existing file. IoError if file does not exist. + Open, + /// Creates a file. IoError if file exists. + Create, + /// Opens an existing file or creates a new one. + OpenOrCreate, + /// Opens an existing file or creates a new one, positioned at EOF. + Append, + /// Opens an existing file, truncating it to 0 bytes. + Truncate, + /// Opens an existing file or creates a new one, truncating it to 0 bytes. + CreateOrTruncate, +} + +enum FileAccess { + Read, + Write, + ReadWrite +} + +pub struct FileStream; + +impl FileStream { + pub fn open<P: PathLike>(_path: &P, + _mode: FileMode, + _access: FileAccess + ) -> Option<FileStream> { + fail!() + } +} + +impl Reader for FileStream { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { + fail!() + } + + fn eof(&mut self) -> bool { + fail!() + } +} + +impl Writer for FileStream { + fn write(&mut self, _v: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Seek for FileStream { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +#[test] +#[ignore] +fn super_simple_smoke_test_lets_go_read_some_files_and_have_a_good_time() { + let message = "it's alright. have a good time"; + let filename = &Path("test.txt"); + let mut outstream = FileStream::open(filename, Create, Read).unwrap(); + outstream.write(message.to_bytes()); +} diff --git a/src/libstd/rt/io/flate.rs b/src/libstd/rt/io/flate.rs new file mode 100644 index 00000000000..db2683dc85d --- /dev/null +++ b/src/libstd/rt/io/flate.rs @@ -0,0 +1,121 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Some various other I/O types + +// NOTE: These ultimately belong somewhere else + +use prelude::*; +use super::*; + +/// A Writer decorator that compresses using the 'deflate' scheme +pub struct DeflateWriter<W> { + inner_writer: W +} + +impl<W: Writer> DeflateWriter<W> { + pub fn new(inner_writer: W) -> DeflateWriter<W> { + DeflateWriter { + inner_writer: inner_writer + } + } +} + +impl<W: Writer> Writer for DeflateWriter<W> { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl<W: Writer> Decorator<W> for DeflateWriter<W> { + fn inner(self) -> W { + match self { + DeflateWriter { inner_writer: w } => w + } + } + + fn inner_ref<'a>(&'a self) -> &'a W { + match *self { + DeflateWriter { inner_writer: ref w } => w + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { + match *self { + DeflateWriter { inner_writer: ref mut w } => w + } + } +} + +/// A Reader decorator that decompresses using the 'deflate' scheme +pub struct InflateReader<R> { + inner_reader: R +} + +impl<R: Reader> InflateReader<R> { + pub fn new(inner_reader: R) -> InflateReader<R> { + InflateReader { + inner_reader: inner_reader + } + } +} + +impl<R: Reader> Reader for InflateReader<R> { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl<R: Reader> Decorator<R> for InflateReader<R> { + fn inner(self) -> R { + match self { + InflateReader { inner_reader: r } => r + } + } + + fn inner_ref<'a>(&'a self) -> &'a R { + match *self { + InflateReader { inner_reader: ref r } => r + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { + match *self { + InflateReader { inner_reader: ref mut r } => r + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::*; + use super::super::mem::*; + use super::super::Decorator; + + #[test] + #[ignore] + fn smoke_test() { + let mem_writer = MemWriter::new(); + let mut deflate_writer = DeflateWriter::new(mem_writer); + let in_msg = "test"; + let in_bytes = in_msg.to_bytes(); + deflate_writer.write(in_bytes); + deflate_writer.flush(); + let buf = deflate_writer.inner().inner(); + let mem_reader = MemReader::new(buf); + let mut inflate_reader = InflateReader::new(mem_reader); + let mut out_bytes = [0, .. 100]; + let bytes_read = inflate_reader.read(out_bytes).get(); + assert_eq!(bytes_read, in_bytes.len()); + let out_msg = str::from_bytes(out_bytes); + assert!(in_msg == out_msg); + } +} diff --git a/src/libstd/rt/io/mem.rs b/src/libstd/rt/io/mem.rs new file mode 100644 index 00000000000..b2701c1fdc3 --- /dev/null +++ b/src/libstd/rt/io/mem.rs @@ -0,0 +1,221 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Readers and Writers for in-memory buffers +//! +//! # XXX +//! +//! * Should probably have something like this for strings. +//! * Should they implement Closable? Would take extra state. + +use prelude::*; +use super::*; +use cmp::min; + +/// Writes to an owned, growable byte vector +pub struct MemWriter { + buf: ~[u8] +} + +impl MemWriter { + pub fn new() -> MemWriter { MemWriter { buf: ~[] } } +} + +impl Writer for MemWriter { + fn write(&mut self, buf: &[u8]) { + self.buf.push_all(buf) + } + + fn flush(&mut self) { /* no-op */ } +} + +impl Seek for MemWriter { + fn tell(&self) -> u64 { self.buf.len() as u64 } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +impl Decorator<~[u8]> for MemWriter { + + fn inner(self) -> ~[u8] { + match self { + MemWriter { buf: buf } => buf + } + } + + fn inner_ref<'a>(&'a self) -> &'a ~[u8] { + match *self { + MemWriter { buf: ref buf } => buf + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { + match *self { + MemWriter { buf: ref mut buf } => buf + } + } +} + +/// Reads from an owned byte vector +pub struct MemReader { + buf: ~[u8], + pos: uint +} + +impl MemReader { + pub fn new(buf: ~[u8]) -> MemReader { + MemReader { + buf: buf, + pos: 0 + } + } +} + +impl Reader for MemReader { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { + { if self.eof() { return None; } } + + let write_len = min(buf.len(), self.buf.len() - self.pos); + { + let input = self.buf.slice(self.pos, self.pos + write_len); + let output = vec::mut_slice(buf, 0, write_len); + assert_eq!(input.len(), output.len()); + vec::bytes::copy_memory(output, input, write_len); + } + self.pos += write_len; + assert!(self.pos <= self.buf.len()); + + return Some(write_len); + } + + fn eof(&mut self) -> bool { self.pos == self.buf.len() } +} + +impl Seek for MemReader { + fn tell(&self) -> u64 { self.pos as u64 } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +impl Decorator<~[u8]> for MemReader { + + fn inner(self) -> ~[u8] { + match self { + MemReader { buf: buf, _ } => buf + } + } + + fn inner_ref<'a>(&'a self) -> &'a ~[u8] { + match *self { + MemReader { buf: ref buf, _ } => buf + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { + match *self { + MemReader { buf: ref mut buf, _ } => buf + } + } +} + + +/// Writes to a fixed-size byte slice +struct BufWriter<'self> { + buf: &'self mut [u8], + pos: uint +} + +impl<'self> BufWriter<'self> { + pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { + BufWriter { + buf: buf, + pos: 0 + } + } +} + +impl<'self> Writer for BufWriter<'self> { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl<'self> Seek for BufWriter<'self> { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + + +/// Reads from a fixed-size byte slice +struct BufReader<'self> { + buf: &'self [u8], + pos: uint +} + +impl<'self> BufReader<'self> { + pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { + BufReader { + buf: buf, + pos: 0 + } + } +} + +impl<'self> Reader for BufReader<'self> { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl<'self> Seek for BufReader<'self> { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::*; + + #[test] + fn test_mem_writer() { + let mut writer = MemWriter::new(); + assert_eq!(writer.tell(), 0); + writer.write([0]); + assert_eq!(writer.tell(), 1); + writer.write([1, 2, 3]); + writer.write([4, 5, 6, 7]); + assert_eq!(writer.tell(), 8); + assert_eq!(writer.inner(), ~[0, 1, 2, 3, 4, 5 , 6, 7]); + } + + #[test] + fn test_mem_reader() { + let mut reader = MemReader::new(~[0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert_eq!(reader.read(buf), Some(0)); + assert_eq!(reader.tell(), 0); + let mut buf = [0]; + assert_eq!(reader.read(buf), Some(1)); + assert_eq!(reader.tell(), 1); + assert_eq!(buf, [0]); + let mut buf = [0, ..4]; + assert_eq!(reader.read(buf), Some(4)); + assert_eq!(reader.tell(), 5); + assert_eq!(buf, [1, 2, 3, 4]); + assert_eq!(reader.read(buf), Some(3)); + assert_eq!(buf.slice(0, 3), [5, 6, 7]); + assert!(reader.eof()); + assert_eq!(reader.read(buf), None); + assert!(reader.eof()); + } +} diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs new file mode 100644 index 00000000000..0ec51a3aa94 --- /dev/null +++ b/src/libstd/rt/io/mod.rs @@ -0,0 +1,504 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! Synchronous I/O + +This module defines the Rust interface for synchronous I/O. +It models byte-oriented input and output with the Reader and Writer traits. +Types that implement both `Reader` and `Writer` and called 'streams', +and automatically implement trait `Stream`. +Implementations are provided for common I/O streams like +file, TCP, UDP, Unix domain sockets. +Readers and Writers may be composed to add capabilities like string +parsing, encoding, and compression. + +This will likely live in core::io, not core::rt::io. + +# Examples + +Some examples of obvious things you might want to do + +* Read lines from stdin + + for stdin().each_line |line| { + println(line) + } + +* Read a complete file to a string, (converting newlines?) + + let contents = File::open("message.txt").read_to_str(); // read_to_str?? + +* Write a line to a file + + let file = File::open("message.txt", Create, Write); + file.write_line("hello, file!"); + +* Iterate over the lines of a file + + do File::open("message.txt").each_line |line| { + println(line) + } + +* Pull the lines of a file into a vector of strings + + let lines = File::open("message.txt").line_iter().to_vec(); + +* Make an simple HTTP request + + let socket = TcpStream::open("localhost:8080"); + socket.write_line("GET / HTTP/1.0"); + socket.write_line(""); + let response = socket.read_to_end(); + +* Connect based on URL? Requires thinking about where the URL type lives + and how to make protocol handlers extensible, e.g. the "tcp" protocol + yields a `TcpStream`. + + connect("tcp://localhost:8080"); + +# Terms + +* Reader - An I/O source, reads bytes into a buffer +* Writer - An I/O sink, writes bytes from a buffer +* Stream - Typical I/O sources like files and sockets are both Readers and Writers, + and are collectively referred to a `streams`. +* Decorator - A Reader or Writer that composes with others to add additional capabilities + such as encoding or decoding + +# Blocking and synchrony + +When discussing I/O you often hear the terms 'synchronous' and +'asynchronous', along with 'blocking' and 'non-blocking' compared and +contrasted. A synchronous I/O interface performs each I/O operation to +completion before proceeding to the next. Synchronous interfaces are +usually used in imperative style as a sequence of commands. An +asynchronous interface allows multiple I/O requests to be issued +simultaneously, without waiting for each to complete before proceeding +to the next. + +Asynchronous interfaces are used to achieve 'non-blocking' I/O. In +traditional single-threaded systems, performing a synchronous I/O +operation means that the program stops all activity (it 'blocks') +until the I/O is complete. Blocking is bad for performance when +there are other computations that could be done. + +Asynchronous interfaces are most often associated with the callback +(continuation-passing) style popularised by node.js. Such systems rely +on all computations being run inside an event loop which maintains a +list of all pending I/O events; when one completes the registered +callback is run and the code that made the I/O request continiues. +Such interfaces achieve non-blocking at the expense of being more +difficult to reason about. + +Rust's I/O interface is synchronous - easy to read - and non-blocking by default. + +Remember that Rust tasks are 'green threads', lightweight threads that +are multiplexed onto a single operating system thread. If that system +thread blocks then no other task may proceed. Rust tasks are +relatively cheap to create, so as long as other tasks are free to +execute then non-blocking code may be written by simply creating a new +task. + +When discussing blocking in regards to Rust's I/O model, we are +concerned with whether performing I/O blocks other Rust tasks from +proceeding. In other words, when a task calls `read`, it must then +wait (or 'sleep', or 'block') until the call to `read` is complete. +During this time, other tasks may or may not be executed, depending on +how `read` is implemented. + + +Rust's default I/O implementation is non-blocking; by cooperating +directly with the task scheduler it arranges to never block progress +of *other* tasks. Under the hood, Rust uses asynchronous I/O via a +per-scheduler (and hence per-thread) event loop. Synchronous I/O +requests are implemented by descheduling the running task and +performing an asynchronous request; the task is only resumed once the +asynchronous request completes. + +For blocking (but possibly more efficient) implementations, look +in the `io::native` module. + +# Error Handling + +I/O is an area where nearly every operation can result in unexpected +errors. It should allow errors to be handled efficiently. +It needs to be convenient to use I/O when you don't care +about dealing with specific errors. + +Rust's I/O employs a combination of techniques to reduce boilerplate +while still providing feedback about errors. The basic strategy: + +* Errors are fatal by default, resulting in task failure +* Errors raise the `io_error` conditon which provides an opportunity to inspect + an IoError object containing details. +* Return values must have a sensible null or zero value which is returned + if a condition is handled successfully. This may be an `Option`, an empty + vector, or other designated error value. +* Common traits are implemented for `Option`, e.g. `impl<R: Reader> Reader for Option<R>`, + so that nullable values do not have to be 'unwrapped' before use. + +These features combine in the API to allow for expressions like +`File::new("diary.txt").write_line("met a girl")` without having to +worry about whether "diary.txt" exists or whether the write +succeeds. As written, if either `new` or `write_line` encounters +an error the task will fail. + +If you wanted to handle the error though you might write + + let mut error = None; + do io_error::cond(|e: IoError| { + error = Some(e); + }).in { + File::new("diary.txt").write_line("met a girl"); + } + + if error.is_some() { + println("failed to write my diary"); + } + +XXX: Need better condition handling syntax + +In this case the condition handler will have the opportunity to +inspect the IoError raised by either the call to `new` or the call to +`write_line`, but then execution will continue. + +So what actually happens if `new` encounters an error? To understand +that it's important to know that what `new` returns is not a `File` +but an `Option<File>`. If the file does not open, and the condition +is handled, then `new` will simply return `None`. Because there is an +implementation of `Writer` (the trait required ultimately required for +types to implement `write_line`) there is no need to inspect or unwrap +the `Option<File>` and we simply call `write_line` on it. If `new` +returned a `None` then the followup call to `write_line` will also +raise an error. + +## Concerns about this strategy + +This structure will encourage a programming style that is prone +to errors similar to null pointer dereferences. +In particular code written to ignore errors and expect conditions to be unhandled +will start passing around null or zero objects when wrapped in a condition handler. + +* XXX: How should we use condition handlers that return values? +* XXX: Should EOF raise default conditions when EOF is not an error? + +# Issues withi/o scheduler affinity, work stealing, task pinning + +# Resource management + +* `close` vs. RAII + +# Paths, URLs and overloaded constructors + + + +# Scope + +In scope for core + +* Url? + +Some I/O things don't belong in core + + - url + - net - `fn connect` + - http + - flate + +Out of scope + +* Async I/O. We'll probably want it eventually + + +# XXX Questions and issues + +* Should default constructors take `Path` or `&str`? `Path` makes simple cases verbose. + Overloading would be nice. +* Add overloading for Path and &str and Url &str +* stdin/err/out +* print, println, etc. +* fsync +* relationship with filesystem querying, Directory, File types etc. +* Rename Reader/Writer to ByteReader/Writer, make Reader/Writer generic? +* Can Port and Chan be implementations of a generic Reader<T>/Writer<T>? +* Trait for things that are both readers and writers, Stream? +* How to handle newline conversion +* String conversion +* File vs. FileStream? File is shorter but could also be used for getting file info + - maybe File is for general file querying and *also* has a static `open` method +* open vs. connect for generic stream opening +* Do we need `close` at all? dtors might be good enough +* How does I/O relate to the Iterator trait? +* std::base64 filters +* Using conditions is a big unknown since we don't have much experience with them +* Too many uses of OtherIoError + +*/ + +use prelude::*; + +// Reexports +pub use self::stdio::stdin; +pub use self::stdio::stdout; +pub use self::stdio::stderr; +pub use self::stdio::print; +pub use self::stdio::println; + +pub use self::file::FileStream; +pub use self::net::ip::IpAddr; +pub use self::net::tcp::TcpListener; +pub use self::net::tcp::TcpStream; +pub use self::net::udp::UdpStream; + +// Some extension traits that all Readers and Writers get. +pub use self::extensions::ReaderUtil; +pub use self::extensions::ReaderByteConversions; +pub use self::extensions::WriterByteConversions; + +/// Synchronous, non-blocking file I/O. +pub mod file; + +/// Synchronous, non-blocking network I/O. +pub mod net { + pub mod tcp; + pub mod udp; + pub mod ip; + #[cfg(unix)] + pub mod unix; + pub mod http; +} + +/// Readers and Writers for memory buffers and strings. +pub mod mem; + +/// Non-blocking access to stdin, stdout, stderr +pub mod stdio; + +/// Implementations for Option +mod option; + +/// Basic stream compression. XXX: Belongs with other flate code +pub mod flate; + +/// Interop between byte streams and pipes. Not sure where it belongs +pub mod comm_adapters; + +/// Extension traits +mod extensions; + +/// Non-I/O things needed by the I/O module +mod support; + +/// Thread-blocking implementations +pub mod native { + /// Posix file I/O + pub mod file; + /// # XXX - implement this + pub mod stdio { } + /// Sockets + /// # XXX - implement this + pub mod net { + pub mod tcp { } + pub mod udp { } + #[cfg(unix)] + pub mod unix { } + } +} + +/// Mock implementations for testing +mod mock; + +/// The default buffer size for various I/O operations +/// XXX: Not pub +pub static DEFAULT_BUF_SIZE: uint = 1024 * 64; + +/// The type passed to I/O condition handlers to indicate error +/// +/// # XXX +/// +/// Is something like this sufficient? It's kind of archaic +pub struct IoError { + kind: IoErrorKind, + desc: &'static str, + detail: Option<~str> +} + +#[deriving(Eq)] +pub enum IoErrorKind { + PreviousIoError, + OtherIoError, + EndOfFile, + FileNotFound, + PermissionDenied, + ConnectionFailed, + Closed, + ConnectionRefused, + ConnectionReset, + BrokenPipe +} + +// XXX: Can't put doc comments on macros +// Raised by `I/O` operations on error. +condition! { + // FIXME (#6009): uncomment `pub` after expansion support lands. + /*pub*/ io_error: super::IoError -> (); +} + +// XXX: Can't put doc comments on macros +// Raised by `read` on error +condition! { + // FIXME (#6009): uncomment `pub` after expansion support lands. + /*pub*/ read_error: super::IoError -> (); +} + +pub trait Reader { + /// Read bytes, up to the length of `buf` and place them in `buf`. + /// Returns the number of bytes read. The number of bytes read my + /// be less than the number requested, even 0. Returns `None` on EOF. + /// + /// # Failure + /// + /// Raises the `read_error` condition on error. If the condition + /// is handled then no guarantee is made about the number of bytes + /// read and the contents of `buf`. If the condition is handled + /// returns `None` (XXX see below). + /// + /// # XXX + /// + /// * Should raise_default error on eof? + /// * If the condition is handled it should still return the bytes read, + /// in which case there's no need to return Option - but then you *have* + /// to install a handler to detect eof. + /// + /// This doesn't take a `len` argument like the old `read`. + /// Will people often need to slice their vectors to call this + /// and will that be annoying? + /// Is it actually possible for 0 bytes to be read successfully? + fn read(&mut self, buf: &mut [u8]) -> Option<uint>; + + /// Return whether the Reader has reached the end of the stream. + /// + /// # Example + /// + /// let reader = FileStream::new() + /// while !reader.eof() { + /// println(reader.read_line()); + /// } + /// + /// # Failue + /// + /// Returns `true` on failure. + fn eof(&mut self) -> bool; +} + +pub trait Writer { + /// Write the given buffer + /// + /// # Failure + /// + /// Raises the `io_error` condition on error + fn write(&mut self, buf: &[u8]); + + /// Flush output + fn flush(&mut self); +} + +pub trait Stream: Reader + Writer { } + +pub enum SeekStyle { + /// Seek from the beginning of the stream + SeekSet, + /// Seek from the end of the stream + SeekEnd, + /// Seek from the current position + SeekCur, +} + +/// # XXX +/// * Are `u64` and `i64` the right choices? +pub trait Seek { + fn tell(&self) -> u64; + + /// Seek to an offset in a stream + /// + /// A successful seek clears the EOF indicator. + /// + /// # XXX + /// + /// * What is the behavior when seeking past the end of a stream? + fn seek(&mut self, pos: i64, style: SeekStyle); +} + +/// A listener is a value that listens for connections +pub trait Listener<S> { + /// Wait for and accept an incoming connection + /// + /// Returns `None` on timeout. + /// + /// # Failure + /// + /// Raises `io_error` condition. If the condition is handled, + /// then `accept` returns `None`. + fn accept(&mut self) -> Option<S>; +} + +/// Common trait for decorator types. +/// +/// Provides accessors to get the inner, 'decorated' values. The I/O library +/// uses decorators to add functionality like compression and encryption to I/O +/// streams. +/// +/// # XXX +/// +/// Is this worth having a trait for? May be overkill +pub trait Decorator<T> { + /// Destroy the decorator and extract the decorated value + /// + /// # XXX + /// + /// Because this takes `self' one could never 'undecorate' a Reader/Writer + /// that has been boxed. Is that ok? This feature is mostly useful for + /// extracting the buffer from MemWriter + fn inner(self) -> T; + + /// Take an immutable reference to the decorated value + fn inner_ref<'a>(&'a self) -> &'a T; + + /// Take a mutable reference to the decorated value + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T; +} + +pub fn standard_error(kind: IoErrorKind) -> IoError { + match kind { + PreviousIoError => { + IoError { + kind: PreviousIoError, + desc: "Failing due to a previous I/O error", + detail: None + } + } + EndOfFile => { + IoError { + kind: EndOfFile, + desc: "End of file", + detail: None + } + } + _ => fail!() + } +} + +pub fn placeholder_error() -> IoError { + IoError { + kind: OtherIoError, + desc: "Placeholder error. You shouldn't be seeing this", + detail: None + } +} \ No newline at end of file diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs new file mode 100644 index 00000000000..31c90336a24 --- /dev/null +++ b/src/libstd/rt/io/native/file.rs @@ -0,0 +1,74 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Blocking posix-based file I/O + +use prelude::*; +use super::super::*; +use libc::{c_int, FILE}; + +#[allow(non_camel_case_types)] +pub type fd_t = c_int; + +// Make this a newtype so we can't do I/O on arbitrary integers +pub struct FileDesc(fd_t); + +impl FileDesc { + /// Create a `FileDesc` from an open C file descriptor. + /// + /// The `FileDesc` takes ownership of the file descriptor + /// and will close it upon destruction. + pub fn new(_fd: fd_t) -> FileDesc { fail!() } +} + +impl Reader for FileDesc { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for FileDesc { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Seek for FileDesc { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +pub struct CFile(*FILE); + +impl CFile { + /// Create a `CFile` from an open `FILE` pointer. + /// + /// The `CFile` takes ownership of the file descriptor + /// and will close it upon destruction. + pub fn new(_file: *FILE) -> CFile { fail!() } +} + +impl Reader for CFile { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for CFile { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Seek for CFile { + fn tell(&self) -> u64 { fail!() } + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} diff --git a/src/libstd/rt/io/net/http.rs b/src/libstd/rt/io/net/http.rs new file mode 100644 index 00000000000..c693cfaab67 --- /dev/null +++ b/src/libstd/rt/io/net/http.rs @@ -0,0 +1,29 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Simple HTTP client and server + +// XXX This should not be in core + +struct HttpServer; + +#[cfg(test)] +mod test { + use unstable::run_in_bare_thread; + + #[test] #[ignore] + fn smoke_test() { + do run_in_bare_thread { + } + + do run_in_bare_thread { + } + } +} diff --git a/src/libstd/rt/io/net/ip.rs b/src/libstd/rt/io/net/ip.rs new file mode 100644 index 00000000000..df1dfe4d38a --- /dev/null +++ b/src/libstd/rt/io/net/ip.rs @@ -0,0 +1,14 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +pub enum IpAddr { + Ipv4(u8, u8, u8, u8, u16), + Ipv6 +} diff --git a/src/libstd/rt/io/net/tcp.rs b/src/libstd/rt/io/net/tcp.rs new file mode 100644 index 00000000000..f7c03c13a58 --- /dev/null +++ b/src/libstd/rt/io/net/tcp.rs @@ -0,0 +1,359 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 option::{Option, Some, None}; +use result::{Ok, Err}; +use rt::io::net::ip::IpAddr; +use rt::io::{Reader, Writer, Listener}; +use rt::io::{io_error, read_error, EndOfFile}; +use rt::rtio::{IoFactory, IoFactoryObject, + RtioTcpListener, RtioTcpListenerObject, + RtioTcpStream, RtioTcpStreamObject}; +use rt::local::Local; + +pub struct TcpStream { + rtstream: ~RtioTcpStreamObject +} + +impl TcpStream { + fn new(s: ~RtioTcpStreamObject) -> TcpStream { + TcpStream { + rtstream: s + } + } + + pub fn connect(addr: IpAddr) -> Option<TcpStream> { + let stream = unsafe { + rtdebug!("borrowing io to connect"); + let io = Local::unsafe_borrow::<IoFactoryObject>(); + rtdebug!("about to connect"); + (*io).tcp_connect(addr) + }; + + match stream { + Ok(s) => { + Some(TcpStream::new(s)) + } + Err(ioerr) => { + rtdebug!("failed to connect: %?", ioerr); + io_error::cond.raise(ioerr); + return None; + } + } + } +} + +impl Reader for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { + let bytes_read = self.rtstream.read(buf); + match bytes_read { + Ok(read) => Some(read), + Err(ioerr) => { + // EOF is indicated by returning None + if ioerr.kind != EndOfFile { + read_error::cond.raise(ioerr); + } + return None; + } + } + } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for TcpStream { + fn write(&mut self, buf: &[u8]) { + let res = self.rtstream.write(buf); + match res { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } + } + + fn flush(&mut self) { fail!() } +} + +pub struct TcpListener { + rtlistener: ~RtioTcpListenerObject, +} + +impl TcpListener { + pub fn bind(addr: IpAddr) -> Option<TcpListener> { + let listener = unsafe { + let io = Local::unsafe_borrow::<IoFactoryObject>(); + (*io).tcp_bind(addr) + }; + match listener { + Ok(l) => { + Some(TcpListener { + rtlistener: l + }) + } + Err(ioerr) => { + io_error::cond.raise(ioerr); + return None; + } + } + } +} + +impl Listener<TcpStream> for TcpListener { + fn accept(&mut self) -> Option<TcpStream> { + let rtstream = self.rtlistener.accept(); + match rtstream { + Ok(s) => { + Some(TcpStream::new(s)) + } + Err(ioerr) => { + io_error::cond.raise(ioerr); + return None; + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use int; + use cell::Cell; + use rt::test::*; + use rt::io::net::ip::Ipv4; + use rt::io::*; + + #[test] #[ignore] + fn bind_error() { + do run_in_newsched_task { + let mut called = false; + do io_error::cond.trap(|e| { + assert!(e.kind == PermissionDenied); + called = true; + }).in { + let addr = Ipv4(0, 0, 0, 0, 1); + let listener = TcpListener::bind(addr); + assert!(listener.is_none()); + } + assert!(called); + } + } + + #[test] + fn connect_error() { + do run_in_newsched_task { + let mut called = false; + do io_error::cond.trap(|e| { + assert!(e.kind == ConnectionRefused); + called = true; + }).in { + let addr = Ipv4(0, 0, 0, 0, 1); + let stream = TcpStream::connect(addr); + assert!(stream.is_none()); + } + assert!(called); + } + } + + #[test] + fn smoke_test() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == 99); + } + + do spawntask_immediately { + let mut stream = TcpStream::connect(addr); + stream.write([99]); + } + } + } + + #[test] + fn read_eof() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + let nread = stream.read(buf); + assert!(nread.is_none()); + } + + do spawntask_immediately { + let _stream = TcpStream::connect(addr); + // Close + } + } + } + + #[test] + fn read_eof_twice() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + let nread = stream.read(buf); + assert!(nread.is_none()); + let nread = stream.read(buf); + assert!(nread.is_none()); + } + + do spawntask_immediately { + let _stream = TcpStream::connect(addr); + // Close + } + } + } + + #[test] + fn write_close() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let buf = [0]; + loop { + let mut stop = false; + do io_error::cond.trap(|e| { + // NB: ECONNRESET on linux, EPIPE on mac + assert!(e.kind == ConnectionReset || e.kind == BrokenPipe); + stop = true; + }).in { + stream.write(buf); + } + if stop { break } + } + } + + do spawntask_immediately { + let _stream = TcpStream::connect(addr); + // Close + } + } + } + + #[test] + fn multiple_connect_serial() { + do run_in_newsched_task { + let addr = next_test_ip4(); + let max = 10; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for max.times { + let mut stream = listener.accept(); + let mut buf = [0]; + stream.read(buf); + assert_eq!(buf[0], 99); + } + } + + do spawntask_immediately { + for max.times { + let mut stream = TcpStream::connect(addr); + stream.write([99]); + } + } + } + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule() { + do run_in_newsched_task { + let addr = next_test_ip4(); + static MAX: int = 10; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for int::range(0, MAX) |i| { + let stream = Cell(listener.accept()); + rtdebug!("accepted"); + // Start another task to handle the connection + do spawntask_immediately { + let mut stream = stream.take(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == i as u8); + rtdebug!("read"); + } + } + } + + connect(0, addr); + + fn connect(i: int, addr: IpAddr) { + if i == MAX { return } + + do spawntask_immediately { + rtdebug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + rtdebug!("writing"); + stream.write([i as u8]); + } + } + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule() { + do run_in_newsched_task { + let addr = next_test_ip4(); + static MAX: int = 10; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for int::range(0, MAX) |_| { + let stream = Cell(listener.accept()); + rtdebug!("accepted"); + // Start another task to handle the connection + do spawntask_later { + let mut stream = stream.take(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == 99); + rtdebug!("read"); + } + } + } + + connect(0, addr); + + fn connect(i: int, addr: IpAddr) { + if i == MAX { return } + + do spawntask_later { + rtdebug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + rtdebug!("writing"); + stream.write([99]); + } + } + } + } + +} diff --git a/src/libstd/rt/io/net/udp.rs b/src/libstd/rt/io/net/udp.rs new file mode 100644 index 00000000000..bb5457e334d --- /dev/null +++ b/src/libstd/rt/io/net/udp.rs @@ -0,0 +1,45 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use super::super::*; +use super::ip::IpAddr; + +pub struct UdpStream; + +impl UdpStream { + pub fn connect(_addr: IpAddr) -> Option<UdpStream> { + fail!() + } +} + +impl Reader for UdpStream { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for UdpStream { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +pub struct UdpListener; + +impl UdpListener { + pub fn bind(_addr: IpAddr) -> Option<UdpListener> { + fail!() + } +} + +impl Listener<UdpStream> for UdpListener { + fn accept(&mut self) -> Option<UdpStream> { fail!() } +} diff --git a/src/libstd/rt/io/net/unix.rs b/src/libstd/rt/io/net/unix.rs new file mode 100644 index 00000000000..b85b7dd059d --- /dev/null +++ b/src/libstd/rt/io/net/unix.rs @@ -0,0 +1,45 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use super::super::*; +use super::super::support::PathLike; + +pub struct UnixStream; + +impl UnixStream { + pub fn connect<P: PathLike>(_path: &P) -> Option<UnixStream> { + fail!() + } +} + +impl Reader for UnixStream { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for UnixStream { + fn write(&mut self, _v: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +pub struct UnixListener; + +impl UnixListener { + pub fn bind<P: PathLike>(_path: &P) -> Option<UnixListener> { + fail!() + } +} + +impl Listener<UnixStream> for UnixListener { + fn accept(&mut self) -> Option<UnixStream> { fail!() } +} diff --git a/src/libstd/rt/io/option.rs b/src/libstd/rt/io/option.rs new file mode 100644 index 00000000000..d71ef55d3ad --- /dev/null +++ b/src/libstd/rt/io/option.rs @@ -0,0 +1,153 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Implementations of I/O traits for the Option type +//! +//! I/O constructors return option types to allow errors to be handled. +//! These implementations allow e.g. `Option<FileStream>` to be used +//! as a `Reader` without unwrapping the option first. +//! +//! # XXX Seek and Close + +use option::*; +use super::{Reader, Writer, Listener}; +use super::{standard_error, PreviousIoError, io_error, read_error, IoError}; + +fn prev_io_error() -> IoError { + standard_error(PreviousIoError) +} + +impl<W: Writer> Writer for Option<W> { + fn write(&mut self, buf: &[u8]) { + match *self { + Some(ref mut writer) => writer.write(buf), + None => io_error::cond.raise(prev_io_error()) + } + } + + fn flush(&mut self) { + match *self { + Some(ref mut writer) => writer.flush(), + None => io_error::cond.raise(prev_io_error()) + } + } +} + +impl<R: Reader> Reader for Option<R> { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { + match *self { + Some(ref mut reader) => reader.read(buf), + None => { + read_error::cond.raise(prev_io_error()); + None + } + } + } + + fn eof(&mut self) -> bool { + match *self { + Some(ref mut reader) => reader.eof(), + None => { + io_error::cond.raise(prev_io_error()); + true + } + } + } +} + +impl<L: Listener<S>, S> Listener<S> for Option<L> { + fn accept(&mut self) -> Option<S> { + match *self { + Some(ref mut listener) => listener.accept(), + None => { + io_error::cond.raise(prev_io_error()); + None + } + } + } +} + +#[cfg(test)] +mod test { + use option::*; + use super::super::mem::*; + use rt::test::*; + use super::super::{PreviousIoError, io_error, read_error}; + + #[test] + fn test_option_writer() { + do run_in_newsched_task { + let mut writer: Option<MemWriter> = Some(MemWriter::new()); + writer.write([0, 1, 2]); + writer.flush(); + assert_eq!(writer.unwrap().inner(), ~[0, 1, 2]); + } + } + + #[test] + fn test_option_writer_error() { + do run_in_newsched_task { + let mut writer: Option<MemWriter> = None; + + let mut called = false; + do io_error::cond.trap(|err| { + assert_eq!(err.kind, PreviousIoError); + called = true; + }).in { + writer.write([0, 0, 0]); + } + assert!(called); + + let mut called = false; + do io_error::cond.trap(|err| { + assert_eq!(err.kind, PreviousIoError); + called = true; + }).in { + writer.flush(); + } + assert!(called); + } + } + + #[test] + fn test_option_reader() { + do run_in_newsched_task { + let mut reader: Option<MemReader> = Some(MemReader::new(~[0, 1, 2, 3])); + let mut buf = [0, 0]; + reader.read(buf); + assert_eq!(buf, [0, 1]); + assert!(!reader.eof()); + } + } + + #[test] + fn test_option_reader_error() { + let mut reader: Option<MemReader> = None; + let mut buf = []; + + let mut called = false; + do read_error::cond.trap(|err| { + assert_eq!(err.kind, PreviousIoError); + called = true; + }).in { + reader.read(buf); + } + assert!(called); + + let mut called = false; + do io_error::cond.trap(|err| { + assert_eq!(err.kind, PreviousIoError); + called = true; + }).in { + assert!(reader.eof()); + } + assert!(called); + } +} diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs new file mode 100644 index 00000000000..247fe954408 --- /dev/null +++ b/src/libstd/rt/io/stdio.rs @@ -0,0 +1,53 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use super::{Reader, Writer}; + +pub fn stdin() -> StdReader { fail!() } + +pub fn stdout() -> StdWriter { fail!() } + +pub fn stderr() -> StdReader { fail!() } + +pub fn print(_s: &str) { fail!() } + +pub fn println(_s: &str) { fail!() } + +pub enum StdStream { + StdIn, + StdOut, + StdErr +} + +pub struct StdReader; + +impl StdReader { + pub fn new(_stream: StdStream) -> StdReader { fail!() } +} + +impl Reader for StdReader { + fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +pub struct StdWriter; + +impl StdWriter { + pub fn new(_stream: StdStream) -> StdWriter { fail!() } +} + +impl Writer for StdWriter { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + diff --git a/src/libstd/rt/io/support.rs b/src/libstd/rt/io/support.rs new file mode 100644 index 00000000000..7bace5d6df2 --- /dev/null +++ b/src/libstd/rt/io/support.rs @@ -0,0 +1,42 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 path::*; + +pub trait PathLike { + fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T; +} + +impl<'self> PathLike for &'self str { + fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T { + f(*self) + } +} + +impl PathLike for Path { + fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T { + let s = self.to_str(); + f(s) + } +} + +#[cfg(test)] +mod test { + use path::*; + use super::PathLike; + + #[test] + fn path_like_smoke_test() { + let expected = "/home"; + let path = Path(expected); + path.path_as_str(|p| assert!(p == expected)); + path.path_as_str(|p| assert!(p == expected)); + } +} diff --git a/src/libstd/rt/local_heap.rs b/src/libstd/rt/local_heap.rs new file mode 100644 index 00000000000..6bf228a1b22 --- /dev/null +++ b/src/libstd/rt/local_heap.rs @@ -0,0 +1,80 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! The local, garbage collected heap + +use libc::{c_void, uintptr_t, size_t}; +use ops::Drop; + +type MemoryRegion = c_void; +type BoxedRegion = c_void; + +pub type OpaqueBox = c_void; +pub type TypeDesc = c_void; + +pub struct LocalHeap { + memory_region: *MemoryRegion, + boxed_region: *BoxedRegion +} + +impl LocalHeap { + pub fn new() -> LocalHeap { + unsafe { + // Don't need synchronization for the single-threaded local heap + let synchronized = false as uintptr_t; + // XXX: These usually come from the environment + let detailed_leaks = false as uintptr_t; + let poison_on_free = false as uintptr_t; + let region = rust_new_memory_region(synchronized, detailed_leaks, poison_on_free); + assert!(region.is_not_null()); + let boxed = rust_new_boxed_region(region, poison_on_free); + assert!(boxed.is_not_null()); + LocalHeap { + memory_region: region, + boxed_region: boxed + } + } + } + + pub fn alloc(&mut self, td: *TypeDesc, size: uint) -> *OpaqueBox { + unsafe { + return rust_boxed_region_malloc(self.boxed_region, td, size as size_t); + } + } + + pub fn free(&mut self, box: *OpaqueBox) { + unsafe { + return rust_boxed_region_free(self.boxed_region, box); + } + } +} + +impl Drop for LocalHeap { + fn finalize(&self) { + unsafe { + rust_delete_boxed_region(self.boxed_region); + rust_delete_memory_region(self.memory_region); + } + } +} + +extern { + fn rust_new_memory_region(synchronized: uintptr_t, + detailed_leaks: uintptr_t, + poison_on_free: uintptr_t) -> *MemoryRegion; + fn rust_delete_memory_region(region: *MemoryRegion); + fn rust_new_boxed_region(region: *MemoryRegion, + poison_on_free: uintptr_t) -> *BoxedRegion; + fn rust_delete_boxed_region(region: *BoxedRegion); + fn rust_boxed_region_malloc(region: *BoxedRegion, + td: *TypeDesc, + size: size_t) -> *OpaqueBox; + fn rust_boxed_region_free(region: *BoxedRegion, box: *OpaqueBox); +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs new file mode 100644 index 00000000000..2fac1df01a4 --- /dev/null +++ b/src/libstd/rt/mod.rs @@ -0,0 +1,243 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! The Rust Runtime, including the task scheduler and I/O + +The `rt` module provides the private runtime infrastructure necessary +to support core language features like the exchange and local heap, +the garbage collector, logging, local data and unwinding. It also +implements the default task scheduler and task model. Initialization +routines are provided for setting up runtime resources in common +configurations, including that used by `rustc` when generating +executables. + +It is intended that the features provided by `rt` can be factored in a +way such that the core library can be built with different 'profiles' +for different use cases, e.g. excluding the task scheduler. A number +of runtime features though are critical to the functioning of the +language and an implementation must be provided regardless of the +execution environment. + +Of foremost importance is the global exchange heap, in the module +`global_heap`. Very little practical Rust code can be written without +access to the global heap. Unlike most of `rt` the global heap is +truly a global resource and generally operates independently of the +rest of the runtime. + +All other runtime features are task-local, including the local heap, +the garbage collector, local storage, logging and the stack unwinder. + +The relationship between `rt` and the rest of the core library is +not entirely clear yet and some modules will be moving into or +out of `rt` as development proceeds. + +Several modules in `core` are clients of `rt`: + +* `core::task` - The user-facing interface to the Rust task model. +* `core::task::local_data` - The interface to local data. +* `core::gc` - The garbage collector. +* `core::unstable::lang` - Miscellaneous lang items, some of which rely on `core::rt`. +* `core::condition` - Uses local data. +* `core::cleanup` - Local heap destruction. +* `core::io` - In the future `core::io` will use an `rt` implementation. +* `core::logging` +* `core::pipes` +* `core::comm` +* `core::stackwalk` + +*/ + +#[doc(hidden)]; + +use ptr::Ptr; + +/// The global (exchange) heap. +pub mod global_heap; + +/// Implementations of language-critical runtime features like @. +pub mod task; + +/// The coroutine task scheduler, built on the `io` event loop. +mod sched; + +/// Synchronous I/O. +#[path = "io/mod.rs"] +pub mod io; + +/// The EventLoop and internal synchronous I/O interface. +mod rtio; + +/// libuv and default rtio implementation. +#[path = "uv/mod.rs"] +pub mod uv; + +/// The Local trait for types that are accessible via thread-local +/// or task-local storage. +pub mod local; + +/// A parallel work-stealing deque. +mod work_queue; + +/// A parallel queue. +mod message_queue; + +/// Stack segments and caching. +mod stack; + +/// CPU context swapping. +mod context; + +/// Bindings to system threading libraries. +mod thread; + +/// The runtime configuration, read from environment variables +pub mod env; + +/// The local, managed heap +mod local_heap; + +/// The Logger trait and implementations +pub mod logging; + +/// Tools for testing the runtime +pub mod test; + +/// Reference counting +pub mod rc; + +/// A simple single-threaded channel type for passing buffered data between +/// scheduler and task context +pub mod tube; + +/// Simple reimplementation of core::comm +pub mod comm; + +// FIXME #5248 shouldn't be pub +/// The runtime needs to be able to put a pointer into thread-local storage. +pub mod local_ptr; + +// FIXME #5248: The import in `sched` doesn't resolve unless this is pub! +/// Bindings to pthread/windows thread-local storage. +pub mod thread_local_storage; + + +/// Set up a default runtime configuration, given compiler-supplied arguments. +/// +/// This is invoked by the `start` _language item_ (unstable::lang) to +/// run a Rust executable. +/// +/// # Arguments +/// +/// * `argc` & `argv` - The argument vector. On Unix this information is used +/// by os::args. +/// * `crate_map` - Runtime information about the executing crate, mostly for logging +/// +/// # Return value +/// +/// The return value is used as the process return code. 0 on success, 101 on error. +pub fn start(_argc: int, _argv: **u8, crate_map: *u8, main: ~fn()) -> int { + + use self::sched::{Scheduler, Coroutine}; + use self::uv::uvio::UvEventLoop; + + init(crate_map); + + let loop_ = ~UvEventLoop::new(); + let mut sched = ~Scheduler::new(loop_); + let main_task = ~Coroutine::new(&mut sched.stack_pool, main); + + sched.enqueue_task(main_task); + sched.run(); + + return 0; +} + +/// One-time runtime initialization. Currently all this does is set up logging +/// based on the RUST_LOG environment variable. +pub fn init(crate_map: *u8) { + logging::init(crate_map); +} + +/// Possible contexts in which Rust code may be executing. +/// Different runtime services are available depending on context. +/// Mostly used for determining if we're using the new scheduler +/// or the old scheduler. +#[deriving(Eq)] +pub enum RuntimeContext { + // Only the exchange heap is available + GlobalContext, + // The scheduler may be accessed + SchedulerContext, + // Full task services, e.g. local heap, unwinding + TaskContext, + // Running in an old-style task + OldTaskContext +} + +/// Determine the current RuntimeContext +pub fn context() -> RuntimeContext { + + use task::rt::rust_task; + use self::local::Local; + use self::sched::Scheduler; + + // XXX: Hitting TLS twice to check if the scheduler exists + // then to check for the task is not good for perf + if unsafe { rust_try_get_task().is_not_null() } { + return OldTaskContext; + } else { + if Local::exists::<Scheduler>() { + let context = ::cell::empty_cell(); + do Local::borrow::<Scheduler> |sched| { + if sched.in_task_context() { + context.put_back(TaskContext); + } else { + context.put_back(SchedulerContext); + } + } + return context.take(); + } else { + return GlobalContext; + } + } + + pub extern { + #[rust_stack] + fn rust_try_get_task() -> *rust_task; + } +} + +#[test] +fn test_context() { + use unstable::run_in_bare_thread; + use self::sched::{Scheduler, Coroutine}; + use rt::uv::uvio::UvEventLoop; + use cell::Cell; + use rt::local::Local; + + assert_eq!(context(), OldTaskContext); + do run_in_bare_thread { + assert_eq!(context(), GlobalContext); + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~do Coroutine::new(&mut sched.stack_pool) { + assert_eq!(context(), TaskContext); + let sched = Local::take::<Scheduler>(); + do sched.deschedule_running_task_and_then() |task| { + assert_eq!(context(), SchedulerContext); + let task = Cell(task); + do Local::borrow::<Scheduler> |sched| { + sched.enqueue_task(task.take()); + } + } + }; + sched.enqueue_task(task); + sched.run(); + } +} diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs new file mode 100644 index 00000000000..4b5eda22ff5 --- /dev/null +++ b/src/libstd/rt/rtio.rs @@ -0,0 +1,45 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 option::*; +use result::*; + +use rt::io::IoError; +use super::io::net::ip::IpAddr; +use rt::uv::uvio; + +// XXX: ~object doesn't work currently so these are some placeholder +// types to use instead +pub type EventLoopObject = uvio::UvEventLoop; +pub type IoFactoryObject = uvio::UvIoFactory; +pub type RtioTcpStreamObject = uvio::UvTcpStream; +pub type RtioTcpListenerObject = uvio::UvTcpListener; + +pub trait EventLoop { + fn run(&mut self); + fn callback(&mut self, ~fn()); + fn callback_ms(&mut self, ms: u64, ~fn()); + /// The asynchronous I/O services. Not all event loops may provide one + fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>; +} + +pub trait IoFactory { + fn tcp_connect(&mut self, addr: IpAddr) -> Result<~RtioTcpStreamObject, IoError>; + fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError>; +} + +pub trait RtioTcpListener { + fn accept(&mut self) -> Result<~RtioTcpStreamObject, IoError>; +} + +pub trait RtioTcpStream { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError>; + fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; +} diff --git a/src/libstd/rt/stack.rs b/src/libstd/rt/stack.rs new file mode 100644 index 00000000000..ec56e65931c --- /dev/null +++ b/src/libstd/rt/stack.rs @@ -0,0 +1,76 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 container::Container; +use ptr::Ptr; +use vec; +use ops::Drop; +use libc::{c_uint, uintptr_t}; + +pub struct StackSegment { + buf: ~[u8], + valgrind_id: c_uint +} + +pub impl StackSegment { + fn new(size: uint) -> StackSegment { + unsafe { + // Crate a block of uninitialized values + let mut stack = vec::with_capacity(size); + vec::raw::set_len(&mut stack, size); + + let mut stk = StackSegment { + buf: stack, + valgrind_id: 0 + }; + + // XXX: Using the FFI to call a C macro. Slow + stk.valgrind_id = rust_valgrind_stack_register(stk.start(), stk.end()); + return stk; + } + } + + /// Point to the low end of the allocated stack + fn start(&self) -> *uint { + vec::raw::to_ptr(self.buf) as *uint + } + + /// Point one word beyond the high end of the allocated stack + fn end(&self) -> *uint { + vec::raw::to_ptr(self.buf).offset(self.buf.len()) as *uint + } +} + +impl Drop for StackSegment { + fn finalize(&self) { + unsafe { + // XXX: Using the FFI to call a C macro. Slow + rust_valgrind_stack_deregister(self.valgrind_id); + } + } +} + +pub struct StackPool(()); + +impl StackPool { + pub fn new() -> StackPool { StackPool(()) } + + fn take_segment(&self, min_size: uint) -> StackSegment { + StackSegment::new(min_size) + } + + fn give_segment(&self, _stack: StackSegment) { + } +} + +extern { + fn rust_valgrind_stack_register(start: *uintptr_t, end: *uintptr_t) -> c_uint; + fn rust_valgrind_stack_deregister(id: c_uint); +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs new file mode 100644 index 00000000000..0314137fc7f --- /dev/null +++ b/src/libstd/rt/task.rs @@ -0,0 +1,230 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Language-level runtime services that should reasonably expected +//! to be available 'everywhere'. Local heaps, GC, unwinding, +//! local storage, and logging. Even a 'freestanding' Rust would likely want +//! to implement this. + +use prelude::*; +use libc::{c_void, uintptr_t}; +use cast::transmute; +use super::sched::Scheduler; +use rt::local::Local; +use super::local_heap::LocalHeap; +use rt::logging::StdErrLogger; + +pub struct Task { + heap: LocalHeap, + gc: GarbageCollector, + storage: LocalStorage, + logger: StdErrLogger, + unwinder: Option<Unwinder>, + destroyed: bool +} + +pub struct GarbageCollector; +pub struct LocalStorage(*c_void, Option<~fn(*c_void)>); + +pub struct Unwinder { + unwinding: bool, +} + +impl Task { + pub fn new() -> Task { + Task { + heap: LocalHeap::new(), + gc: GarbageCollector, + storage: LocalStorage(ptr::null(), None), + logger: StdErrLogger, + unwinder: Some(Unwinder { unwinding: false }), + destroyed: false + } + } + + pub fn without_unwinding() -> Task { + Task { + heap: LocalHeap::new(), + gc: GarbageCollector, + storage: LocalStorage(ptr::null(), None), + logger: StdErrLogger, + unwinder: None, + destroyed: false + } + } + + pub fn run(&mut self, f: &fn()) { + // This is just an assertion that `run` was called unsafely + // and this instance of Task is still accessible. + do Local::borrow::<Task> |task| { + assert!(ptr::ref_eq(task, self)); + } + + match self.unwinder { + Some(ref mut unwinder) => { + // If there's an unwinder then set up the catch block + unwinder.try(f); + } + None => { + // Otherwise, just run the body + f() + } + } + self.destroy(); + } + + /// Must be called manually before finalization to clean up + /// thread-local resources. Some of the routines here expect + /// Task to be available recursively so this must be + /// called unsafely, without removing Task from + /// thread-local-storage. + fn destroy(&mut self) { + // This is just an assertion that `destroy` was called unsafely + // and this instance of Task is still accessible. + do Local::borrow::<Task> |task| { + assert!(ptr::ref_eq(task, self)); + } + match self.storage { + LocalStorage(ptr, Some(ref dtor)) => { + (*dtor)(ptr) + } + _ => () + } + self.destroyed = true; + } +} + +impl Drop for Task { + fn finalize(&self) { assert!(self.destroyed) } +} + +// Just a sanity check to make sure we are catching a Rust-thrown exception +static UNWIND_TOKEN: uintptr_t = 839147; + +impl Unwinder { + pub fn try(&mut self, f: &fn()) { + use sys::Closure; + + unsafe { + let closure: Closure = transmute(f); + let code = transmute(closure.code); + let env = transmute(closure.env); + + let token = rust_try(try_fn, code, env); + assert!(token == 0 || token == UNWIND_TOKEN); + } + + extern fn try_fn(code: *c_void, env: *c_void) { + unsafe { + let closure: Closure = Closure { + code: transmute(code), + env: transmute(env), + }; + let closure: &fn() = transmute(closure); + closure(); + } + } + + extern { + #[rust_stack] + fn rust_try(f: *u8, code: *c_void, data: *c_void) -> uintptr_t; + } + } + + pub fn begin_unwind(&mut self) -> ! { + self.unwinding = true; + unsafe { + rust_begin_unwind(UNWIND_TOKEN); + return transmute(()); + } + extern { + fn rust_begin_unwind(token: uintptr_t); + } + } +} + +#[cfg(test)] +mod test { + use rt::test::*; + + #[test] + fn local_heap() { + do run_in_newsched_task() { + let a = @5; + let b = a; + assert!(*a == 5); + assert!(*b == 5); + } + } + + #[test] + fn tls() { + use local_data::*; + do run_in_newsched_task() { + unsafe { + fn key(_x: @~str) { } + local_data_set(key, @~"data"); + assert!(*local_data_get(key).get() == ~"data"); + fn key2(_x: @~str) { } + local_data_set(key2, @~"data"); + assert!(*local_data_get(key2).get() == ~"data"); + } + } + } + + #[test] + fn unwind() { + do run_in_newsched_task() { + let result = spawntask_try(||()); + assert!(result.is_ok()); + let result = spawntask_try(|| fail!()); + assert!(result.is_err()); + } + } + + #[test] + fn rng() { + do run_in_newsched_task() { + use rand::{rng, Rng}; + let mut r = rng(); + let _ = r.next(); + } + } + + #[test] + fn logging() { + do run_in_newsched_task() { + info!("here i am. logging in a newsched task"); + } + } + + #[test] + fn comm_oneshot() { + use comm::*; + + do run_in_newsched_task { + let (port, chan) = oneshot(); + send_one(chan, 10); + assert!(recv_one(port) == 10); + } + } + + #[test] + fn comm_stream() { + use comm::*; + + do run_in_newsched_task() { + let (port, chan) = stream(); + chan.send(10); + assert!(port.recv() == 10); + } + } +} + diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs new file mode 100644 index 00000000000..c60ae2bfeff --- /dev/null +++ b/src/libstd/rt/test.rs @@ -0,0 +1,192 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 uint; +use option::*; +use cell::Cell; +use result::{Result, Ok, Err}; +use super::io::net::ip::{IpAddr, Ipv4}; +use rt::task::Task; +use rt::thread::Thread; +use rt::local::Local; + +/// Creates a new scheduler in a new thread and runs a task in it, +/// then waits for the scheduler to exit. Failure of the task +/// will abort the process. +pub fn run_in_newsched_task(f: ~fn()) { + use super::sched::*; + use unstable::run_in_bare_thread; + use rt::uv::uvio::UvEventLoop; + + let f = Cell(f); + + do run_in_bare_thread { + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f.take()); + sched.enqueue_task(task); + sched.run(); + } +} + +/// Test tasks will abort on failure instead of unwinding +pub fn spawntask(f: ~fn()) { + use super::sched::*; + + let mut sched = Local::take::<Scheduler>(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f); + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + let sched = Local::take::<Scheduler>(); + sched.schedule_new_task(task.take()); + } +} + +/// Create a new task and run it right now. Aborts on failure +pub fn spawntask_immediately(f: ~fn()) { + use super::sched::*; + + let mut sched = Local::take::<Scheduler>(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f); + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + do Local::borrow::<Scheduler> |sched| { + sched.enqueue_task(task.take()); + } + } +} + +/// Create a new task and run it right now. Aborts on failure +pub fn spawntask_later(f: ~fn()) { + use super::sched::*; + + let mut sched = Local::take::<Scheduler>(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f); + + sched.enqueue_task(task); + Local::put(sched); +} + +/// Spawn a task and either run it immediately or run it later +pub fn spawntask_random(f: ~fn()) { + use super::sched::*; + use rand::{Rand, rng}; + + let mut rng = rng(); + let run_now: bool = Rand::rand(&mut rng); + + let mut sched = Local::take::<Scheduler>(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f); + + if run_now { + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + do Local::borrow::<Scheduler> |sched| { + sched.enqueue_task(task.take()); + } + } + } else { + sched.enqueue_task(task); + Local::put(sched); + } +} + + +/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed +pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { + use cell::Cell; + use super::sched::*; + use task; + use unstable::finally::Finally; + + // Our status variables will be filled in from the scheduler context + let mut failed = false; + let failed_ptr: *mut bool = &mut failed; + + // Switch to the scheduler + let f = Cell(Cell(f)); + let sched = Local::take::<Scheduler>(); + do sched.deschedule_running_task_and_then() |old_task| { + let old_task = Cell(old_task); + let f = f.take(); + let mut sched = Local::take::<Scheduler>(); + let new_task = ~do Coroutine::new(&mut sched.stack_pool) { + do (|| { + (f.take())() + }).finally { + // Check for failure then resume the parent task + unsafe { *failed_ptr = task::failing(); } + let sched = Local::take::<Scheduler>(); + do sched.switch_running_tasks_and_then(old_task.take()) |new_task| { + let new_task = Cell(new_task); + do Local::borrow::<Scheduler> |sched| { + sched.enqueue_task(new_task.take()); + } + } + } + }; + + sched.resume_task_immediately(new_task); + } + + if !failed { Ok(()) } else { Err(()) } +} + +// Spawn a new task in a new scheduler and return a thread handle. +pub fn spawntask_thread(f: ~fn()) -> Thread { + use rt::sched::*; + use rt::uv::uvio::UvEventLoop; + + let f = Cell(f); + let thread = do Thread::start { + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~Coroutine::with_task(&mut sched.stack_pool, + ~Task::without_unwinding(), + f.take()); + sched.enqueue_task(task); + sched.run(); + }; + return thread; +} + +/// Get a port number, starting at 9600, for use in tests +pub fn next_test_port() -> u16 { + unsafe { + return rust_dbg_next_port() as u16; + } + extern { + fn rust_dbg_next_port() -> ::libc::uintptr_t; + } +} + +/// Get a unique localhost:port pair starting at 9600 +pub fn next_test_ip4() -> IpAddr { + Ipv4(127, 0, 0, 1, next_test_port()) +} + +/// Get a constant that represents the number of times to repeat stress tests. Default 1. +pub fn stress_factor() -> uint { + use os::getenv; + + match getenv("RUST_RT_STRESS") { + Some(val) => uint::from_str(val).get(), + None => 1 + } +} + diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs new file mode 100644 index 00000000000..0f1ae09bd94 --- /dev/null +++ b/src/libstd/rt/thread.rs @@ -0,0 +1,44 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 libc; +use ops::Drop; + +#[allow(non_camel_case_types)] // runtime type +type raw_thread = libc::c_void; + +pub struct Thread { + main: ~fn(), + raw_thread: *raw_thread +} + +pub impl Thread { + fn start(main: ~fn()) -> Thread { + fn substart(main: &~fn()) -> *raw_thread { + unsafe { rust_raw_thread_start(main) } + } + let raw = substart(&main); + Thread { + main: main, + raw_thread: raw + } + } +} + +impl Drop for Thread { + fn finalize(&self) { + unsafe { rust_raw_thread_join_delete(self.raw_thread) } + } +} + +extern { + pub unsafe fn rust_raw_thread_start(f: &(~fn())) -> *raw_thread; + pub unsafe fn rust_raw_thread_join_delete(thread: *raw_thread); +} diff --git a/src/libstd/rt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs new file mode 100644 index 00000000000..7187d2db41c --- /dev/null +++ b/src/libstd/rt/thread_local_storage.rs @@ -0,0 +1,100 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 libc::c_void; +#[cfg(unix)] +use libc::c_int; +#[cfg(unix)] +use ptr::null; +#[cfg(windows)] +use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; + +#[cfg(unix)] +pub type Key = pthread_key_t; + +#[cfg(unix)] +pub unsafe fn create(key: &mut Key) { + assert_eq!(0, pthread_key_create(key, null())); +} + +#[cfg(unix)] +pub unsafe fn set(key: Key, value: *mut c_void) { + assert_eq!(0, pthread_setspecific(key, value)); +} + +#[cfg(unix)] +pub unsafe fn get(key: Key) -> *mut c_void { + pthread_getspecific(key) +} + +#[cfg(target_os="macos")] +#[allow(non_camel_case_types)] // foreign type +type pthread_key_t = ::libc::c_ulong; + +#[cfg(target_os="linux")] +#[cfg(target_os="freebsd")] +#[cfg(target_os="android")] +#[allow(non_camel_case_types)] // foreign type +type pthread_key_t = ::libc::c_uint; + +#[cfg(unix)] +extern { + #[fast_ffi] + fn pthread_key_create(key: *mut pthread_key_t, dtor: *u8) -> c_int; + #[fast_ffi] + fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int; + #[fast_ffi] + fn pthread_getspecific(key: pthread_key_t) -> *mut c_void; +} + +#[cfg(windows)] +pub type Key = DWORD; + +#[cfg(windows)] +pub unsafe fn create(key: &mut Key) { + static TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; + *key = unsafe { TlsAlloc() }; + assert!(*key != TLS_OUT_OF_INDEXES); +} + +#[cfg(windows)] +pub unsafe fn set(key: Key, value: *mut c_void) { + unsafe { assert!(0 != TlsSetValue(key, value)) } +} + +#[cfg(windows)] +pub unsafe fn get(key: Key) -> *mut c_void { + TlsGetValue(key) +} + +#[cfg(windows)] +#[abi = "stdcall"] +extern "stdcall" { + fn TlsAlloc() -> DWORD; + fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; + fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; +} + +#[test] +fn tls_smoke_test() { + use cast::transmute; + unsafe { + let mut key = 0; + let value = ~20; + create(&mut key); + set(key, transmute(value)); + let value: ~int = transmute(get(key)); + assert_eq!(value, ~20); + let value = ~30; + set(key, transmute(value)); + let value: ~int = transmute(get(key)); + assert_eq!(value, ~30); + } +} diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs new file mode 100644 index 00000000000..2d145055097 --- /dev/null +++ b/src/libstd/rt/uv/file.rs @@ -0,0 +1,48 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use ptr::null; +use libc::c_void; +use rt::uv::{Request, NativeHandle, Loop, FsCallback}; +use rt::uv::uvll; +use rt::uv::uvll::*; + +pub struct FsRequest(*uvll::uv_fs_t); +impl Request for FsRequest; + +impl FsRequest { + fn new() -> FsRequest { + let fs_req = unsafe { malloc_req(UV_FS) }; + assert!(fs_req.is_not_null()); + let fs_req = fs_req as *uvll::uv_write_t; + unsafe { uvll::set_data_for_req(fs_req, null::<()>()); } + NativeHandle::from_native_handle(fs_req) + } + + fn delete(self) { + unsafe { free_req(self.native_handle() as *c_void) } + } + + fn open(&mut self, _loop_: &Loop, _cb: FsCallback) { + } + + fn close(&mut self, _loop_: &Loop, _cb: FsCallback) { + } +} + +impl NativeHandle<*uvll::uv_fs_t> for FsRequest { + fn from_native_handle(handle: *uvll:: uv_fs_t) -> FsRequest { + FsRequest(handle) + } + fn native_handle(&self) -> *uvll::uv_fs_t { + match self { &FsRequest(ptr) => ptr } + } +} diff --git a/src/libstd/rt/uv/mod.rs b/src/libstd/rt/uv/mod.rs new file mode 100644 index 00000000000..2bd657fd864 --- /dev/null +++ b/src/libstd/rt/uv/mod.rs @@ -0,0 +1,420 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Bindings to libuv, along with the default implementation of `core::rt::rtio`. + +UV types consist of the event loop (Loop), Watchers, Requests and +Callbacks. + +Watchers and Requests encapsulate pointers to uv *handles*, which have +subtyping relationships with each other. This subtyping is reflected +in the bindings with explicit or implicit coercions. For example, an +upcast from TcpWatcher to StreamWatcher is done with +`tcp_watcher.as_stream()`. In other cases a callback on a specific +type of watcher will be passed a watcher of a supertype. + +Currently all use of Request types (connect/write requests) are +encapsulated in the bindings and don't need to be dealt with by the +caller. + +# Safety note + +Due to the complex lifecycle of uv handles, as well as compiler bugs, +this module is not memory safe and requires explicit memory management, +via `close` and `delete` methods. + +*/ + +use container::Container; +use option::*; +use str::raw::from_c_str; +use to_str::ToStr; +use ptr::Ptr; +use libc; +use vec; +use ptr; +use cast; +use str; +use libc::{c_void, c_int, size_t, malloc, free}; +use cast::transmute; +use ptr::null; +use unstable::finally::Finally; + +use rt::io::IoError; + +#[cfg(test)] use unstable::run_in_bare_thread; + +pub use self::file::FsRequest; +pub use self::net::{StreamWatcher, TcpWatcher}; +pub use self::idle::IdleWatcher; +pub use self::timer::TimerWatcher; + +/// The implementation of `rtio` for libuv +pub mod uvio; + +/// C bindings to libuv +pub mod uvll; + +pub mod file; +pub mod net; +pub mod idle; +pub mod timer; + +/// XXX: Loop(*handle) is buggy with destructors. Normal structs +/// with dtors may not be destructured, but tuple structs can, +/// but the results are not correct. +pub struct Loop { + handle: *uvll::uv_loop_t +} + +/// The trait implemented by uv 'watchers' (handles). Watchers are +/// non-owning wrappers around the uv handles and are not completely +/// safe - there may be multiple instances for a single underlying +/// handle. Watchers are generally created, then `start`ed, `stop`ed +/// and `close`ed, but due to their complex life cycle may not be +/// entirely memory safe if used in unanticipated patterns. +pub trait Watcher { } + +pub trait Request { } + +/// A type that wraps a native handle +pub trait NativeHandle<T> { + pub fn from_native_handle(T) -> Self; + pub fn native_handle(&self) -> T; +} + +pub impl Loop { + fn new() -> Loop { + let handle = unsafe { uvll::loop_new() }; + assert!(handle.is_not_null()); + NativeHandle::from_native_handle(handle) + } + + fn run(&mut self) { + unsafe { uvll::run(self.native_handle()) }; + } + + fn close(&mut self) { + unsafe { uvll::loop_delete(self.native_handle()) }; + } +} + +impl NativeHandle<*uvll::uv_loop_t> for Loop { + fn from_native_handle(handle: *uvll::uv_loop_t) -> Loop { + Loop { handle: handle } + } + fn native_handle(&self) -> *uvll::uv_loop_t { + self.handle + } +} + +// XXX: The uv alloc callback also has a *uv_handle_t arg +pub type AllocCallback = ~fn(uint) -> Buf; +pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option<UvError>); +pub type NullCallback = ~fn(); +pub type IdleCallback = ~fn(IdleWatcher, Option<UvError>); +pub type ConnectionCallback = ~fn(StreamWatcher, Option<UvError>); +pub type FsCallback = ~fn(FsRequest, Option<UvError>); +pub type TimerCallback = ~fn(TimerWatcher, Option<UvError>); + + +/// Callbacks used by StreamWatchers, set as custom data on the foreign handle +struct WatcherData { + read_cb: Option<ReadCallback>, + write_cb: Option<ConnectionCallback>, + connect_cb: Option<ConnectionCallback>, + close_cb: Option<NullCallback>, + alloc_cb: Option<AllocCallback>, + idle_cb: Option<IdleCallback>, + timer_cb: Option<TimerCallback> +} + +pub trait WatcherInterop { + fn event_loop(&self) -> Loop; + fn install_watcher_data(&mut self); + fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData; + fn drop_watcher_data(&mut self); +} + +impl<H, W: Watcher + NativeHandle<*H>> WatcherInterop for W { + /// Get the uv event loop from a Watcher + pub fn event_loop(&self) -> Loop { + unsafe { + let handle = self.native_handle(); + let loop_ = uvll::get_loop_for_uv_handle(handle); + NativeHandle::from_native_handle(loop_) + } + } + + pub fn install_watcher_data(&mut self) { + unsafe { + let data = ~WatcherData { + read_cb: None, + write_cb: None, + connect_cb: None, + close_cb: None, + alloc_cb: None, + idle_cb: None, + timer_cb: None + }; + let data = transmute::<~WatcherData, *c_void>(data); + uvll::set_data_for_uv_handle(self.native_handle(), data); + } + } + + pub fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData { + unsafe { + let data = uvll::get_data_for_uv_handle(self.native_handle()); + let data = transmute::<&*c_void, &mut ~WatcherData>(&data); + return &mut **data; + } + } + + pub fn drop_watcher_data(&mut self) { + unsafe { + let data = uvll::get_data_for_uv_handle(self.native_handle()); + let _data = transmute::<*c_void, ~WatcherData>(data); + uvll::set_data_for_uv_handle(self.native_handle(), null::<()>()); + } + } +} + +// XXX: Need to define the error constants like EOF so they can be +// compared to the UvError type + +pub struct UvError(uvll::uv_err_t); + +pub impl UvError { + + fn name(&self) -> ~str { + unsafe { + let inner = match self { &UvError(ref a) => a }; + let name_str = uvll::err_name(inner); + assert!(name_str.is_not_null()); + from_c_str(name_str) + } + } + + fn desc(&self) -> ~str { + unsafe { + let inner = match self { &UvError(ref a) => a }; + let desc_str = uvll::strerror(inner); + assert!(desc_str.is_not_null()); + from_c_str(desc_str) + } + } + + fn is_eof(&self) -> bool { + self.code == uvll::EOF + } +} + +impl ToStr for UvError { + fn to_str(&self) -> ~str { + fmt!("%s: %s", self.name(), self.desc()) + } +} + +#[test] +fn error_smoke_test() { + let err = uvll::uv_err_t { code: 1, sys_errno_: 1 }; + let err: UvError = UvError(err); + assert_eq!(err.to_str(), ~"EOF: end of file"); +} + +pub fn last_uv_error<H, W: Watcher + NativeHandle<*H>>(watcher: &W) -> UvError { + unsafe { + let loop_ = watcher.event_loop(); + UvError(uvll::last_error(loop_.native_handle())) + } +} + +pub fn uv_error_to_io_error(uverr: UvError) -> IoError { + + // XXX: Could go in str::raw + unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { + let s = s as *u8; + let mut curr = s, len = 0u; + while *curr != 0u8 { + len += 1u; + curr = ptr::offset(s, len); + } + + str::raw::buf_as_slice(s, len, |d| cast::transmute(d)) + } + + + unsafe { + // Importing error constants + use rt::uv::uvll::*; + use rt::io::*; + + // uv error descriptions are static + let c_desc = uvll::strerror(&*uverr); + let desc = c_str_to_static_slice(c_desc); + + let kind = match uverr.code { + UNKNOWN => OtherIoError, + OK => OtherIoError, + EOF => EndOfFile, + EACCES => PermissionDenied, + ECONNREFUSED => ConnectionRefused, + ECONNRESET => ConnectionReset, + EPIPE => BrokenPipe, + _ => { + rtdebug!("uverr.code %u", uverr.code as uint); + // XXX: Need to map remaining uv error types + OtherIoError + } + }; + + IoError { + kind: kind, + desc: desc, + detail: None + } + } +} + +/// Given a uv handle, convert a callback status to a UvError +// XXX: Follow the pattern below by parameterizing over T: Watcher, not T +pub fn status_to_maybe_uv_error<T>(handle: *T, status: c_int) -> Option<UvError> { + if status != -1 { + None + } else { + unsafe { + rtdebug!("handle: %x", handle as uint); + let loop_ = uvll::get_loop_for_uv_handle(handle); + rtdebug!("loop: %x", loop_ as uint); + let err = uvll::last_error(loop_); + Some(UvError(err)) + } + } +} + +/// The uv buffer type +pub type Buf = uvll::uv_buf_t; + +/// Borrow a slice to a Buf +pub fn slice_to_uv_buf(v: &[u8]) -> Buf { + let data = vec::raw::to_ptr(v); + unsafe { uvll::buf_init(data, v.len()) } +} + +// XXX: Do these conversions without copying + +/// Transmute an owned vector to a Buf +pub fn vec_to_uv_buf(v: ~[u8]) -> Buf { + unsafe { + let data = malloc(v.len() as size_t) as *u8; + assert!(data.is_not_null()); + do vec::as_imm_buf(v) |b, l| { + let data = data as *mut u8; + ptr::copy_memory(data, b, l) + } + uvll::buf_init(data, v.len()) + } +} + +/// Transmute a Buf that was once a ~[u8] back to ~[u8] +pub fn vec_from_uv_buf(buf: Buf) -> Option<~[u8]> { + if !(buf.len == 0 && buf.base.is_null()) { + let v = unsafe { vec::from_buf(buf.base, buf.len as uint) }; + unsafe { free(buf.base as *c_void) }; + return Some(v); + } else { + // No buffer + rtdebug!("No buffer!"); + return None; + } +} + +#[test] +fn test_slice_to_uv_buf() { + let slice = [0, .. 20]; + let buf = slice_to_uv_buf(slice); + + assert!(buf.len == 20); + + unsafe { + let base = transmute::<*u8, *mut u8>(buf.base); + (*base) = 1; + (*ptr::mut_offset(base, 1)) = 2; + } + + assert!(slice[0] == 1); + assert!(slice[1] == 2); +} + + +#[test] +fn loop_smoke_test() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + loop_.run(); + loop_.close(); + } +} + +#[test] +#[ignore(reason = "valgrind - loop destroyed before watcher?")] +fn idle_new_then_close() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let idle_watcher = { IdleWatcher::new(&mut loop_) }; + idle_watcher.close(||()); + } +} + +#[test] +fn idle_smoke_test() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let mut idle_watcher = { IdleWatcher::new(&mut loop_) }; + let mut count = 10; + let count_ptr: *mut int = &mut count; + do idle_watcher.start |idle_watcher, status| { + let mut idle_watcher = idle_watcher; + assert!(status.is_none()); + if unsafe { *count_ptr == 10 } { + idle_watcher.stop(); + idle_watcher.close(||()); + } else { + unsafe { *count_ptr = *count_ptr + 1; } + } + } + loop_.run(); + loop_.close(); + assert_eq!(count, 10); + } +} + +#[test] +fn idle_start_stop_start() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let mut idle_watcher = { IdleWatcher::new(&mut loop_) }; + do idle_watcher.start |idle_watcher, status| { + let mut idle_watcher = idle_watcher; + assert!(status.is_none()); + idle_watcher.stop(); + do idle_watcher.start |idle_watcher, status| { + assert!(status.is_none()); + let mut idle_watcher = idle_watcher; + idle_watcher.stop(); + idle_watcher.close(||()); + } + } + loop_.run(); + loop_.close(); + } +} diff --git a/src/libstd/rt/uv/net.rs b/src/libstd/rt/uv/net.rs new file mode 100644 index 00000000000..68b871e6b31 --- /dev/null +++ b/src/libstd/rt/uv/net.rs @@ -0,0 +1,436 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 prelude::*; +use libc::{size_t, ssize_t, c_int, c_void}; +use rt::uv::uvll; +use rt::uv::uvll::*; +use rt::uv::{AllocCallback, ConnectionCallback, ReadCallback}; +use rt::uv::{Loop, Watcher, Request, UvError, Buf, NativeHandle, NullCallback, + status_to_maybe_uv_error}; +use rt::io::net::ip::{IpAddr, Ipv4, Ipv6}; +use rt::uv::last_uv_error; + +fn ip4_as_uv_ip4<T>(addr: IpAddr, f: &fn(*sockaddr_in) -> T) -> T { + match addr { + Ipv4(a, b, c, d, p) => { + unsafe { + let addr = malloc_ip4_addr(fmt!("%u.%u.%u.%u", + a as uint, + b as uint, + c as uint, + d as uint), p as int); + do (|| { + f(addr) + }).finally { + free_ip4_addr(addr); + } + } + } + Ipv6 => fail!() + } +} + +// uv_stream t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t +// and uv_file_t +pub struct StreamWatcher(*uvll::uv_stream_t); +impl Watcher for StreamWatcher { } + +pub impl StreamWatcher { + + fn read_start(&mut self, alloc: AllocCallback, cb: ReadCallback) { + { + let data = self.get_watcher_data(); + data.alloc_cb = Some(alloc); + data.read_cb = Some(cb); + } + + let handle = self.native_handle(); + unsafe { uvll::read_start(handle, alloc_cb, read_cb); } + + extern fn alloc_cb(stream: *uvll::uv_stream_t, suggested_size: size_t) -> Buf { + let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream); + let data = stream_watcher.get_watcher_data(); + let alloc_cb = data.alloc_cb.get_ref(); + return (*alloc_cb)(suggested_size as uint); + } + + extern fn read_cb(stream: *uvll::uv_stream_t, nread: ssize_t, buf: Buf) { + rtdebug!("buf addr: %x", buf.base as uint); + rtdebug!("buf len: %d", buf.len as int); + let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream); + let data = stream_watcher.get_watcher_data(); + let cb = data.read_cb.get_ref(); + let status = status_to_maybe_uv_error(stream, nread as c_int); + (*cb)(stream_watcher, nread as int, buf, status); + } + } + + fn read_stop(&mut self) { + // It would be nice to drop the alloc and read callbacks here, + // but read_stop may be called from inside one of them and we + // would end up freeing the in-use environment + let handle = self.native_handle(); + unsafe { uvll::read_stop(handle); } + } + + fn write(&mut self, buf: Buf, cb: ConnectionCallback) { + { + let data = self.get_watcher_data(); + assert!(data.write_cb.is_none()); + data.write_cb = Some(cb); + } + + let req = WriteRequest::new(); + let bufs = [buf]; + unsafe { + assert!(0 == uvll::write(req.native_handle(), + self.native_handle(), + bufs, write_cb)); + } + + extern fn write_cb(req: *uvll::uv_write_t, status: c_int) { + let write_request: WriteRequest = NativeHandle::from_native_handle(req); + let mut stream_watcher = write_request.stream(); + write_request.delete(); + let cb = { + let data = stream_watcher.get_watcher_data(); + let cb = data.write_cb.swap_unwrap(); + cb + }; + let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); + cb(stream_watcher, status); + } + } + + fn accept(&mut self, stream: StreamWatcher) { + let self_handle = self.native_handle() as *c_void; + let stream_handle = stream.native_handle() as *c_void; + unsafe { + assert_eq!(0, uvll::accept(self_handle, stream_handle)); + } + } + + fn close(self, cb: NullCallback) { + { + let mut this = self; + let data = this.get_watcher_data(); + assert!(data.close_cb.is_none()); + data.close_cb = Some(cb); + } + + unsafe { uvll::close(self.native_handle(), close_cb); } + + extern fn close_cb(handle: *uvll::uv_stream_t) { + let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); + { + let data = stream_watcher.get_watcher_data(); + data.close_cb.swap_unwrap()(); + } + stream_watcher.drop_watcher_data(); + unsafe { free_handle(handle as *c_void) } + } + } +} + +impl NativeHandle<*uvll::uv_stream_t> for StreamWatcher { + fn from_native_handle( + handle: *uvll::uv_stream_t) -> StreamWatcher { + StreamWatcher(handle) + } + fn native_handle(&self) -> *uvll::uv_stream_t { + match self { &StreamWatcher(ptr) => ptr } + } +} + +pub struct TcpWatcher(*uvll::uv_tcp_t); +impl Watcher for TcpWatcher { } + +pub impl TcpWatcher { + fn new(loop_: &mut Loop) -> TcpWatcher { + unsafe { + let handle = malloc_handle(UV_TCP); + assert!(handle.is_not_null()); + assert_eq!(0, uvll::tcp_init(loop_.native_handle(), handle)); + let mut watcher: TcpWatcher = NativeHandle::from_native_handle(handle); + watcher.install_watcher_data(); + return watcher; + } + } + + fn bind(&mut self, address: IpAddr) -> Result<(), UvError> { + match address { + Ipv4(*) => { + do ip4_as_uv_ip4(address) |addr| { + let result = unsafe { + uvll::tcp_bind(self.native_handle(), addr) + }; + if result == 0 { + Ok(()) + } else { + Err(last_uv_error(self)) + } + } + } + _ => fail!() + } + } + + fn connect(&mut self, address: IpAddr, cb: ConnectionCallback) { + unsafe { + assert!(self.get_watcher_data().connect_cb.is_none()); + self.get_watcher_data().connect_cb = Some(cb); + + let connect_handle = ConnectRequest::new().native_handle(); + match address { + Ipv4(*) => { + do ip4_as_uv_ip4(address) |addr| { + rtdebug!("connect_t: %x", connect_handle as uint); + assert!(0 == uvll::tcp_connect(connect_handle, + self.native_handle(), + addr, connect_cb)); + } + } + _ => fail!() + } + + extern fn connect_cb(req: *uvll::uv_connect_t, status: c_int) { + rtdebug!("connect_t: %x", req as uint); + let connect_request: ConnectRequest = NativeHandle::from_native_handle(req); + let mut stream_watcher = connect_request.stream(); + connect_request.delete(); + let cb: ConnectionCallback = { + let data = stream_watcher.get_watcher_data(); + data.connect_cb.swap_unwrap() + }; + let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); + cb(stream_watcher, status); + } + } + } + + fn listen(&mut self, cb: ConnectionCallback) { + { + let data = self.get_watcher_data(); + assert!(data.connect_cb.is_none()); + data.connect_cb = Some(cb); + } + + unsafe { + static BACKLOG: c_int = 128; // XXX should be configurable + // XXX: This can probably fail + assert!(0 == uvll::listen(self.native_handle(), + BACKLOG, connection_cb)); + } + + extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) { + rtdebug!("connection_cb"); + let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); + let data = stream_watcher.get_watcher_data(); + let cb = data.connect_cb.get_ref(); + let status = status_to_maybe_uv_error(handle, status); + (*cb)(stream_watcher, status); + } + } + + fn as_stream(&self) -> StreamWatcher { + NativeHandle::from_native_handle(self.native_handle() as *uvll::uv_stream_t) + } +} + +impl NativeHandle<*uvll::uv_tcp_t> for TcpWatcher { + fn from_native_handle(handle: *uvll::uv_tcp_t) -> TcpWatcher { + TcpWatcher(handle) + } + fn native_handle(&self) -> *uvll::uv_tcp_t { + match self { &TcpWatcher(ptr) => ptr } + } +} + +// uv_connect_t is a subclass of uv_req_t +struct ConnectRequest(*uvll::uv_connect_t); +impl Request for ConnectRequest { } + +impl ConnectRequest { + + fn new() -> ConnectRequest { + let connect_handle = unsafe { + malloc_req(UV_CONNECT) + }; + assert!(connect_handle.is_not_null()); + let connect_handle = connect_handle as *uvll::uv_connect_t; + ConnectRequest(connect_handle) + } + + fn stream(&self) -> StreamWatcher { + unsafe { + let stream_handle = uvll::get_stream_handle_from_connect_req(self.native_handle()); + NativeHandle::from_native_handle(stream_handle) + } + } + + fn delete(self) { + unsafe { free_req(self.native_handle() as *c_void) } + } +} + +impl NativeHandle<*uvll::uv_connect_t> for ConnectRequest { + fn from_native_handle( + handle: *uvll:: uv_connect_t) -> ConnectRequest { + ConnectRequest(handle) + } + fn native_handle(&self) -> *uvll::uv_connect_t { + match self { &ConnectRequest(ptr) => ptr } + } +} + +pub struct WriteRequest(*uvll::uv_write_t); + +impl Request for WriteRequest { } + +pub impl WriteRequest { + + fn new() -> WriteRequest { + let write_handle = unsafe { + malloc_req(UV_WRITE) + }; + assert!(write_handle.is_not_null()); + let write_handle = write_handle as *uvll::uv_write_t; + WriteRequest(write_handle) + } + + fn stream(&self) -> StreamWatcher { + unsafe { + let stream_handle = uvll::get_stream_handle_from_write_req(self.native_handle()); + NativeHandle::from_native_handle(stream_handle) + } + } + + fn delete(self) { + unsafe { free_req(self.native_handle() as *c_void) } + } +} + +impl NativeHandle<*uvll::uv_write_t> for WriteRequest { + fn from_native_handle(handle: *uvll:: uv_write_t) -> WriteRequest { + WriteRequest(handle) + } + fn native_handle(&self) -> *uvll::uv_write_t { + match self { &WriteRequest(ptr) => ptr } + } +} + + +#[cfg(test)] +mod test { + use super::*; + use util::ignore; + use cell::Cell; + use vec; + use unstable::run_in_bare_thread; + use rt::thread::Thread; + use rt::test::*; + use rt::uv::{Loop, AllocCallback}; + use rt::uv::{vec_from_uv_buf, vec_to_uv_buf, slice_to_uv_buf}; + + #[test] + fn connect_close() { + do run_in_bare_thread() { + let mut loop_ = Loop::new(); + let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; + // Connect to a port where nobody is listening + let addr = next_test_ip4(); + do tcp_watcher.connect(addr) |stream_watcher, status| { + rtdebug!("tcp_watcher.connect!"); + assert!(status.is_some()); + assert_eq!(status.get().name(), ~"ECONNREFUSED"); + stream_watcher.close(||()); + } + loop_.run(); + loop_.close(); + } + } + + #[test] + fn listen() { + do run_in_bare_thread() { + static MAX: int = 10; + let mut loop_ = Loop::new(); + let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) }; + let addr = next_test_ip4(); + server_tcp_watcher.bind(addr); + let loop_ = loop_; + rtdebug!("listening"); + do server_tcp_watcher.listen |server_stream_watcher, status| { + rtdebug!("listened!"); + assert!(status.is_none()); + let mut server_stream_watcher = server_stream_watcher; + let mut loop_ = loop_; + let client_tcp_watcher = TcpWatcher::new(&mut loop_); + let mut client_tcp_watcher = client_tcp_watcher.as_stream(); + server_stream_watcher.accept(client_tcp_watcher); + let count_cell = Cell(0); + let server_stream_watcher = server_stream_watcher; + rtdebug!("starting read"); + let alloc: AllocCallback = |size| { + vec_to_uv_buf(vec::from_elem(size, 0)) + }; + do client_tcp_watcher.read_start(alloc) + |stream_watcher, nread, buf, status| { + + rtdebug!("i'm reading!"); + let buf = vec_from_uv_buf(buf); + let mut count = count_cell.take(); + if status.is_none() { + rtdebug!("got %d bytes", nread); + let buf = buf.unwrap(); + for buf.slice(0, nread as uint).each |byte| { + assert!(*byte == count as u8); + rtdebug!("%u", *byte as uint); + count += 1; + } + } else { + assert_eq!(count, MAX); + do stream_watcher.close { + server_stream_watcher.close(||()); + } + } + count_cell.put_back(count); + } + } + + let _client_thread = do Thread::start { + rtdebug!("starting client thread"); + let mut loop_ = Loop::new(); + let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; + do tcp_watcher.connect(addr) |stream_watcher, status| { + rtdebug!("connecting"); + assert!(status.is_none()); + let mut stream_watcher = stream_watcher; + let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9]; + let buf = slice_to_uv_buf(msg); + let msg_cell = Cell(msg); + do stream_watcher.write(buf) |stream_watcher, status| { + rtdebug!("writing"); + assert!(status.is_none()); + let msg_cell = Cell(msg_cell.take()); + stream_watcher.close(||ignore(msg_cell.take())); + } + } + loop_.run(); + loop_.close(); + }; + + let mut loop_ = loop_; + loop_.run(); + loop_.close(); + } + } +} diff --git a/src/libstd/rt/uvio.rs b/src/libstd/rt/uvio.rs new file mode 100644 index 00000000000..24bffd8d1cd --- /dev/null +++ b/src/libstd/rt/uvio.rs @@ -0,0 +1,459 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 option::*; +use result::*; + +use super::io::net::ip::IpAddr; +use super::uv::*; +use super::rtio::*; +use ops::Drop; +use old_iter::CopyableIter; +use cell::{Cell, empty_cell}; +use cast::transmute; +use super::sched::{Scheduler, local_sched}; + +#[cfg(test)] use container::Container; +#[cfg(test)] use uint; +#[cfg(test)] use unstable::run_in_bare_thread; +#[cfg(test)] use super::test::*; + +pub struct UvEventLoop { + uvio: UvIoFactory +} + +pub impl UvEventLoop { + fn new() -> UvEventLoop { + UvEventLoop { + uvio: UvIoFactory(Loop::new()) + } + } + + /// A convenience constructor + fn new_scheduler() -> Scheduler { + Scheduler::new(~UvEventLoop::new()) + } +} + +impl Drop for UvEventLoop { + fn finalize(&self) { + // XXX: Need mutable finalizer + let this = unsafe { + transmute::<&UvEventLoop, &mut UvEventLoop>(self) + }; + this.uvio.uv_loop().close(); + } +} + +impl EventLoop for UvEventLoop { + + fn run(&mut self) { + self.uvio.uv_loop().run(); + } + + fn callback(&mut self, f: ~fn()) { + let mut idle_watcher = IdleWatcher::new(self.uvio.uv_loop()); + do idle_watcher.start |idle_watcher, status| { + assert!(status.is_none()); + let mut idle_watcher = idle_watcher; + idle_watcher.stop(); + idle_watcher.close(); + f(); + } + } + + fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> { + Some(&mut self.uvio) + } +} + +#[test] +fn test_callback_run_once() { + do run_in_bare_thread { + let mut event_loop = UvEventLoop::new(); + let mut count = 0; + let count_ptr: *mut int = &mut count; + do event_loop.callback { + unsafe { *count_ptr += 1 } + } + event_loop.run(); + assert!(count == 1); + } +} + +pub struct UvIoFactory(Loop); + +pub impl UvIoFactory { + fn uv_loop<'a>(&'a mut self) -> &'a mut Loop { + match self { &UvIoFactory(ref mut ptr) => ptr } + } +} + +impl IoFactory for UvIoFactory { + // Connect to an address and return a new stream + // NB: This blocks the task waiting on the connection. + // It would probably be better to return a future + fn connect(&mut self, addr: IpAddr) -> Option<~StreamObject> { + // Create a cell in the task to hold the result. We will fill + // the cell before resuming the task. + let result_cell = empty_cell(); + let result_cell_ptr: *Cell<Option<~StreamObject>> = &result_cell; + + let scheduler = local_sched::take(); + assert!(scheduler.in_task_context()); + + // Block this task and take ownership, switch to scheduler context + do scheduler.deschedule_running_task_and_then |task| { + + rtdebug!("connect: entered scheduler context"); + do local_sched::borrow |scheduler| { + assert!(!scheduler.in_task_context()); + } + let mut tcp_watcher = TcpWatcher::new(self.uv_loop()); + let task_cell = Cell(task); + + // Wait for a connection + do tcp_watcher.connect(addr) |stream_watcher, status| { + rtdebug!("connect: in connect callback"); + let maybe_stream = if status.is_none() { + rtdebug!("status is none"); + Some(~UvStream(stream_watcher)) + } else { + rtdebug!("status is some"); + stream_watcher.close(||()); + None + }; + + // Store the stream in the task's stack + unsafe { (*result_cell_ptr).put_back(maybe_stream); } + + // Context switch + let scheduler = local_sched::take(); + scheduler.resume_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + return result_cell.take(); + } + + fn bind(&mut self, addr: IpAddr) -> Option<~TcpListenerObject> { + let mut watcher = TcpWatcher::new(self.uv_loop()); + watcher.bind(addr); + return Some(~UvTcpListener(watcher)); + } +} + +pub struct UvTcpListener(TcpWatcher); + +impl UvTcpListener { + fn watcher(&self) -> TcpWatcher { + match self { &UvTcpListener(w) => w } + } + + fn close(&self) { + // XXX: Need to wait until close finishes before returning + self.watcher().as_stream().close(||()); + } +} + +impl Drop for UvTcpListener { + fn finalize(&self) { + // XXX: Again, this never gets called. Use .close() instead + //self.watcher().as_stream().close(||()); + } +} + +impl TcpListener for UvTcpListener { + + fn listen(&mut self) -> Option<~StreamObject> { + rtdebug!("entering listen"); + let result_cell = empty_cell(); + let result_cell_ptr: *Cell<Option<~StreamObject>> = &result_cell; + + let server_tcp_watcher = self.watcher(); + + let scheduler = local_sched::take(); + assert!(scheduler.in_task_context()); + + do scheduler.deschedule_running_task_and_then |task| { + let task_cell = Cell(task); + let mut server_tcp_watcher = server_tcp_watcher; + do server_tcp_watcher.listen |server_stream_watcher, status| { + let maybe_stream = if status.is_none() { + let mut server_stream_watcher = server_stream_watcher; + let mut loop_ = loop_from_watcher(&server_stream_watcher); + let client_tcp_watcher = TcpWatcher::new(&mut loop_).as_stream(); + // XXX: Needs to be surfaced in interface + server_stream_watcher.accept(client_tcp_watcher); + Some(~UvStream::new(client_tcp_watcher)) + } else { + None + }; + + unsafe { (*result_cell_ptr).put_back(maybe_stream); } + + rtdebug!("resuming task from listen"); + // Context switch + let scheduler = local_sched::take(); + scheduler.resume_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + return result_cell.take(); + } +} + +pub struct UvStream(StreamWatcher); + +impl UvStream { + fn new(watcher: StreamWatcher) -> UvStream { + UvStream(watcher) + } + + fn watcher(&self) -> StreamWatcher { + match self { &UvStream(w) => w } + } + + // XXX: finalize isn't working for ~UvStream??? + fn close(&self) { + // XXX: Need to wait until this finishes before returning + self.watcher().close(||()); + } +} + +impl Drop for UvStream { + fn finalize(&self) { + rtdebug!("closing stream"); + //self.watcher().close(||()); + } +} + +impl Stream for UvStream { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, ()> { + let result_cell = empty_cell(); + let result_cell_ptr: *Cell<Result<uint, ()>> = &result_cell; + + let scheduler = local_sched::take(); + assert!(scheduler.in_task_context()); + let watcher = self.watcher(); + let buf_ptr: *&mut [u8] = &buf; + do scheduler.deschedule_running_task_and_then |task| { + rtdebug!("read: entered scheduler context"); + do local_sched::borrow |scheduler| { + assert!(!scheduler.in_task_context()); + } + let mut watcher = watcher; + let task_cell = Cell(task); + // XXX: We shouldn't reallocate these callbacks every + // call to read + let alloc: AllocCallback = |_| unsafe { + slice_to_uv_buf(*buf_ptr) + }; + do watcher.read_start(alloc) |watcher, nread, _buf, status| { + + // Stop reading so that no read callbacks are + // triggered before the user calls `read` again. + // XXX: Is there a performance impact to calling + // stop here? + let mut watcher = watcher; + watcher.read_stop(); + + let result = if status.is_none() { + assert!(nread >= 0); + Ok(nread as uint) + } else { + Err(()) + }; + + unsafe { (*result_cell_ptr).put_back(result); } + + let scheduler = local_sched::take(); + scheduler.resume_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + return result_cell.take(); + } + + fn write(&mut self, buf: &[u8]) -> Result<(), ()> { + let result_cell = empty_cell(); + let result_cell_ptr: *Cell<Result<(), ()>> = &result_cell; + let scheduler = local_sched::take(); + assert!(scheduler.in_task_context()); + let watcher = self.watcher(); + let buf_ptr: *&[u8] = &buf; + do scheduler.deschedule_running_task_and_then |task| { + let mut watcher = watcher; + let task_cell = Cell(task); + let buf = unsafe { &*buf_ptr }; + // XXX: OMGCOPIES + let buf = buf.to_vec(); + do watcher.write(buf) |_watcher, status| { + let result = if status.is_none() { + Ok(()) + } else { + Err(()) + }; + + unsafe { (*result_cell_ptr).put_back(result); } + + let scheduler = local_sched::take(); + scheduler.resume_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + return result_cell.take(); + } +} + +#[test] +fn test_simple_io_no_connect() { + do run_in_newsched_task { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let addr = next_test_ip4(); + let maybe_chan = io.connect(addr); + assert!(maybe_chan.is_none()); + } +} + +#[test] +fn test_simple_tcp_server_and_client() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + // Start the server first so it's listening when we connect + do spawntask_immediately { + unsafe { + let io = local_sched::unsafe_borrow_io(); + let mut listener = io.bind(addr).unwrap(); + let mut stream = listener.listen().unwrap(); + let mut buf = [0, .. 2048]; + let nread = stream.read(buf).unwrap(); + assert!(nread == 8); + for uint::range(0, nread) |i| { + rtdebug!("%u", buf[i] as uint); + assert!(buf[i] == i as u8); + } + stream.close(); + listener.close(); + } + } + + do spawntask_immediately { + unsafe { + let io = local_sched::unsafe_borrow_io(); + let mut stream = io.connect(addr).unwrap(); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.close(); + } + } + } +} + +#[test] #[ignore(reason = "busted")] +fn test_read_and_block() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let mut listener = io.bind(addr).unwrap(); + let mut stream = listener.listen().unwrap(); + let mut buf = [0, .. 2048]; + + let expected = 32; + let mut current = 0; + let mut reads = 0; + + while current < expected { + let nread = stream.read(buf).unwrap(); + for uint::range(0, nread) |i| { + let val = buf[i] as uint; + assert!(val == current % 8); + current += 1; + } + reads += 1; + + let scheduler = local_sched::take(); + // Yield to the other task in hopes that it + // will trigger a read callback while we are + // not ready for it + do scheduler.deschedule_running_task_and_then |task| { + let task = Cell(task); + do local_sched::borrow |scheduler| { + scheduler.task_queue.push_back(task.take()); + } + } + } + + // Make sure we had multiple reads + assert!(reads > 1); + + stream.close(); + listener.close(); + } + + do spawntask_immediately { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let mut stream = io.connect(addr).unwrap(); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.close(); + } + + } +} + +#[test] +fn test_read_read_read() { + do run_in_newsched_task { + let addr = next_test_ip4(); + static MAX: uint = 500000; + + do spawntask_immediately { + unsafe { + let io = local_sched::unsafe_borrow_io(); + let mut listener = io.bind(addr).unwrap(); + let mut stream = listener.listen().unwrap(); + let buf = [1, .. 2048]; + let mut total_bytes_written = 0; + while total_bytes_written < MAX { + stream.write(buf); + total_bytes_written += buf.len(); + } + stream.close(); + listener.close(); + } + } + + do spawntask_immediately { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let mut stream = io.connect(addr).unwrap(); + let mut buf = [0, .. 2048]; + let mut total_bytes_read = 0; + while total_bytes_read < MAX { + let nread = stream.read(buf).unwrap(); + rtdebug!("read %u bytes", nread as uint); + total_bytes_read += nread; + for uint::range(0, nread) |i| { + assert!(buf[i] == 1); + } + } + rtdebug!("read %u bytes total", total_bytes_read as uint); + stream.close(); + } + } +} diff --git a/src/libstd/rt/uvll.rs b/src/libstd/rt/uvll.rs new file mode 100644 index 00000000000..0d298bde6b5 --- /dev/null +++ b/src/libstd/rt/uvll.rs @@ -0,0 +1,443 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + * Low-level bindings to the libuv library. + * + * This module contains a set of direct, 'bare-metal' wrappers around + * the libuv C-API. + * + * We're not bothering yet to redefine uv's structs as Rust structs + * because they are quite large and change often between versions. + * The maintenance burden is just too high. Instead we use the uv's + * `uv_handle_size` and `uv_req_size` to find the correct size of the + * structs and allocate them on the heap. This can be revisited later. + * + * There are also a collection of helper functions to ease interacting + * with the low-level API. + * + * As new functionality, existant in uv.h, is added to the rust stdlib, + * the mappings should be added in this module. + */ + +#[allow(non_camel_case_types)]; // C types + +use libc::{size_t, c_int, c_uint, c_void, c_char, uintptr_t}; +use libc::{malloc, free}; +use prelude::*; + +pub struct uv_err_t { + code: c_int, + sys_errno_: c_int +} + +pub struct uv_buf_t { + base: *u8, + len: libc::size_t, +} + +pub type uv_handle_t = c_void; +pub type uv_loop_t = c_void; +pub type uv_idle_t = c_void; +pub type uv_tcp_t = c_void; +pub type uv_connect_t = c_void; +pub type uv_write_t = c_void; +pub type uv_async_t = c_void; +pub type uv_timer_t = c_void; +pub type uv_stream_t = c_void; +pub type uv_fs_t = c_void; + +pub type uv_idle_cb = *u8; + +pub type sockaddr_in = c_void; +pub type sockaddr_in6 = c_void; + +#[deriving(Eq)] +pub enum uv_handle_type { + UV_UNKNOWN_HANDLE, + UV_ASYNC, + UV_CHECK, + UV_FS_EVENT, + UV_FS_POLL, + UV_HANDLE, + UV_IDLE, + UV_NAMED_PIPE, + UV_POLL, + UV_PREPARE, + UV_PROCESS, + UV_STREAM, + UV_TCP, + UV_TIMER, + UV_TTY, + UV_UDP, + UV_SIGNAL, + UV_FILE, + UV_HANDLE_TYPE_MAX +} + +#[deriving(Eq)] +pub enum uv_req_type { + UV_UNKNOWN_REQ, + UV_REQ, + UV_CONNECT, + UV_WRITE, + UV_SHUTDOWN, + UV_UDP_SEND, + UV_FS, + UV_WORK, + UV_GETADDRINFO, + UV_REQ_TYPE_MAX +} + +pub unsafe fn malloc_handle(handle: uv_handle_type) -> *c_void { + assert!(handle != UV_UNKNOWN_HANDLE && handle != UV_HANDLE_TYPE_MAX); + let size = rust_uv_handle_size(handle as uint); + let p = malloc(size); + assert!(p.is_not_null()); + return p; +} + +pub unsafe fn free_handle(v: *c_void) { + free(v) +} + +pub unsafe fn malloc_req(req: uv_req_type) -> *c_void { + assert!(req != UV_UNKNOWN_REQ && req != UV_REQ_TYPE_MAX); + let size = rust_uv_req_size(req as uint); + let p = malloc(size); + assert!(p.is_not_null()); + return p; +} + +pub unsafe fn free_req(v: *c_void) { + free(v) +} + +#[test] +fn handle_sanity_check() { + unsafe { + assert!(UV_HANDLE_TYPE_MAX as uint == rust_uv_handle_type_max()); + } +} + +#[test] +fn request_sanity_check() { + unsafe { + assert!(UV_REQ_TYPE_MAX as uint == rust_uv_req_type_max()); + } +} + +pub unsafe fn loop_new() -> *c_void { + return rust_uv_loop_new(); +} + +pub unsafe fn loop_delete(loop_handle: *c_void) { + rust_uv_loop_delete(loop_handle); +} + +pub unsafe fn run(loop_handle: *c_void) { + rust_uv_run(loop_handle); +} + +pub unsafe fn close<T>(handle: *T, cb: *u8) { + rust_uv_close(handle as *c_void, cb); +} + +pub unsafe fn walk(loop_handle: *c_void, cb: *u8, arg: *c_void) { + rust_uv_walk(loop_handle, cb, arg); +} + +pub unsafe fn idle_new() -> *uv_idle_t { + rust_uv_idle_new() +} + +pub unsafe fn idle_delete(handle: *uv_idle_t) { + rust_uv_idle_delete(handle) +} + +pub unsafe fn idle_init(loop_handle: *uv_loop_t, handle: *uv_idle_t) -> c_int { + rust_uv_idle_init(loop_handle, handle) +} + +pub unsafe fn idle_start(handle: *uv_idle_t, cb: uv_idle_cb) -> c_int { + rust_uv_idle_start(handle, cb) +} + +pub unsafe fn idle_stop(handle: *uv_idle_t) -> c_int { + rust_uv_idle_stop(handle) +} + +pub unsafe fn tcp_init(loop_handle: *c_void, handle: *uv_tcp_t) -> c_int { + return rust_uv_tcp_init(loop_handle, handle); +} + +// FIXME ref #2064 +pub unsafe fn tcp_connect(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + addr_ptr: *sockaddr_in, + after_connect_cb: *u8) -> c_int { + return rust_uv_tcp_connect(connect_ptr, tcp_handle_ptr, + after_connect_cb, addr_ptr); +} +// FIXME ref #2064 +pub unsafe fn tcp_connect6(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + addr_ptr: *sockaddr_in6, + after_connect_cb: *u8) -> c_int { + return rust_uv_tcp_connect6(connect_ptr, tcp_handle_ptr, + after_connect_cb, addr_ptr); +} +// FIXME ref #2064 +pub unsafe fn tcp_bind(tcp_server_ptr: *uv_tcp_t, addr_ptr: *sockaddr_in) -> c_int { + return rust_uv_tcp_bind(tcp_server_ptr, addr_ptr); +} +// FIXME ref #2064 +pub unsafe fn tcp_bind6(tcp_server_ptr: *uv_tcp_t, addr_ptr: *sockaddr_in6) -> c_int { + return rust_uv_tcp_bind6(tcp_server_ptr, addr_ptr); +} + +pub unsafe fn tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, name: *sockaddr_in) -> c_int { + return rust_uv_tcp_getpeername(tcp_handle_ptr, name); +} + +pub unsafe fn tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, name: *sockaddr_in6) ->c_int { + return rust_uv_tcp_getpeername6(tcp_handle_ptr, name); +} + +pub unsafe fn listen<T>(stream: *T, backlog: c_int, cb: *u8) -> c_int { + return rust_uv_listen(stream as *c_void, backlog, cb); +} + +pub unsafe fn accept(server: *c_void, client: *c_void) -> c_int { + return rust_uv_accept(server as *c_void, client as *c_void); +} + +pub unsafe fn write<T>(req: *uv_write_t, stream: *T, buf_in: &[uv_buf_t], cb: *u8) -> c_int { + let buf_ptr = vec::raw::to_ptr(buf_in); + let buf_cnt = buf_in.len() as i32; + return rust_uv_write(req as *c_void, stream as *c_void, buf_ptr, buf_cnt, cb); +} +pub unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8, on_read: *u8) -> c_int { + return rust_uv_read_start(stream as *c_void, on_alloc, on_read); +} + +pub unsafe fn read_stop(stream: *uv_stream_t) -> c_int { + return rust_uv_read_stop(stream as *c_void); +} + +pub unsafe fn last_error(loop_handle: *c_void) -> uv_err_t { + return rust_uv_last_error(loop_handle); +} + +pub unsafe fn strerror(err: *uv_err_t) -> *c_char { + return rust_uv_strerror(err); +} +pub unsafe fn err_name(err: *uv_err_t) -> *c_char { + return rust_uv_err_name(err); +} + +pub unsafe fn async_init(loop_handle: *c_void, async_handle: *uv_async_t, cb: *u8) -> c_int { + return rust_uv_async_init(loop_handle, async_handle, cb); +} + +pub unsafe fn async_send(async_handle: *uv_async_t) { + return rust_uv_async_send(async_handle); +} +pub unsafe fn buf_init(input: *u8, len: uint) -> uv_buf_t { + let out_buf = uv_buf_t { base: ptr::null(), len: 0 as size_t }; + let out_buf_ptr = ptr::to_unsafe_ptr(&out_buf); + rust_uv_buf_init(out_buf_ptr, input, len as size_t); + return out_buf; +} + +pub unsafe fn timer_init(loop_ptr: *c_void, timer_ptr: *uv_timer_t) -> c_int { + return rust_uv_timer_init(loop_ptr, timer_ptr); +} +pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint, + repeat: uint) -> c_int { + return rust_uv_timer_start(timer_ptr, cb, timeout as c_uint, repeat as c_uint); +} +pub unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> c_int { + return rust_uv_timer_stop(timer_ptr); +} + +pub unsafe fn malloc_ip4_addr(ip: &str, port: int) -> *sockaddr_in { + do str::as_c_str(ip) |ip_buf| { + rust_uv_ip4_addrp(ip_buf as *u8, port as libc::c_int) + } +} +pub unsafe fn malloc_ip6_addr(ip: &str, port: int) -> *sockaddr_in6 { + do str::as_c_str(ip) |ip_buf| { + rust_uv_ip6_addrp(ip_buf as *u8, port as libc::c_int) + } +} + +pub unsafe fn free_ip4_addr(addr: *sockaddr_in) { + rust_uv_free_ip4_addr(addr); +} + +pub unsafe fn free_ip6_addr(addr: *sockaddr_in6) { + rust_uv_free_ip6_addr(addr); +} + +// data access helpers +pub unsafe fn get_loop_for_uv_handle<T>(handle: *T) -> *c_void { + return rust_uv_get_loop_for_uv_handle(handle as *c_void); +} +pub unsafe fn get_stream_handle_from_connect_req(connect: *uv_connect_t) -> *uv_stream_t { + return rust_uv_get_stream_handle_from_connect_req(connect); +} +pub unsafe fn get_stream_handle_from_write_req(write_req: *uv_write_t) -> *uv_stream_t { + return rust_uv_get_stream_handle_from_write_req(write_req); +} +pub unsafe fn get_data_for_uv_loop(loop_ptr: *c_void) -> *c_void { + rust_uv_get_data_for_uv_loop(loop_ptr) +} +pub unsafe fn set_data_for_uv_loop(loop_ptr: *c_void, data: *c_void) { + rust_uv_set_data_for_uv_loop(loop_ptr, data); +} +pub unsafe fn get_data_for_uv_handle<T>(handle: *T) -> *c_void { + return rust_uv_get_data_for_uv_handle(handle as *c_void); +} +pub unsafe fn set_data_for_uv_handle<T, U>(handle: *T, data: *U) { + rust_uv_set_data_for_uv_handle(handle as *c_void, data as *c_void); +} +pub unsafe fn get_data_for_req<T>(req: *T) -> *c_void { + return rust_uv_get_data_for_req(req as *c_void); +} +pub unsafe fn set_data_for_req<T, U>(req: *T, data: *U) { + rust_uv_set_data_for_req(req as *c_void, data as *c_void); +} +pub unsafe fn get_base_from_buf(buf: uv_buf_t) -> *u8 { + return rust_uv_get_base_from_buf(buf); +} +pub unsafe fn get_len_from_buf(buf: uv_buf_t) -> size_t { + return rust_uv_get_len_from_buf(buf); +} +pub unsafe fn malloc_buf_base_of(suggested_size: size_t) -> *u8 { + return rust_uv_malloc_buf_base_of(suggested_size); +} +pub unsafe fn free_base_of_buf(buf: uv_buf_t) { + rust_uv_free_base_of_buf(buf); +} + +pub unsafe fn get_last_err_info(uv_loop: *c_void) -> ~str { + let err = last_error(uv_loop); + let err_ptr = ptr::to_unsafe_ptr(&err); + let err_name = str::raw::from_c_str(err_name(err_ptr)); + let err_msg = str::raw::from_c_str(strerror(err_ptr)); + return fmt!("LIBUV ERROR: name: %s msg: %s", + err_name, err_msg); +} + +pub unsafe fn get_last_err_data(uv_loop: *c_void) -> uv_err_data { + let err = last_error(uv_loop); + let err_ptr = ptr::to_unsafe_ptr(&err); + let err_name = str::raw::from_c_str(err_name(err_ptr)); + let err_msg = str::raw::from_c_str(strerror(err_ptr)); + uv_err_data { err_name: err_name, err_msg: err_msg } +} + +pub struct uv_err_data { + err_name: ~str, + err_msg: ~str, +} + +extern { + + fn rust_uv_handle_size(type_: uintptr_t) -> size_t; + fn rust_uv_req_size(type_: uintptr_t) -> size_t; + fn rust_uv_handle_type_max() -> uintptr_t; + fn rust_uv_req_type_max() -> uintptr_t; + + // libuv public API + fn rust_uv_loop_new() -> *c_void; + fn rust_uv_loop_delete(lp: *c_void); + fn rust_uv_run(loop_handle: *c_void); + fn rust_uv_close(handle: *c_void, cb: *u8); + fn rust_uv_walk(loop_handle: *c_void, cb: *u8, arg: *c_void); + + fn rust_uv_idle_new() -> *uv_idle_t; + fn rust_uv_idle_delete(handle: *uv_idle_t); + fn rust_uv_idle_init(loop_handle: *uv_loop_t, handle: *uv_idle_t) -> c_int; + fn rust_uv_idle_start(handle: *uv_idle_t, cb: uv_idle_cb) -> c_int; + fn rust_uv_idle_stop(handle: *uv_idle_t) -> c_int; + + fn rust_uv_async_send(handle: *uv_async_t); + fn rust_uv_async_init(loop_handle: *c_void, + async_handle: *uv_async_t, + cb: *u8) -> c_int; + fn rust_uv_tcp_init(loop_handle: *c_void, handle_ptr: *uv_tcp_t) -> c_int; + // FIXME ref #2604 .. ? + fn rust_uv_buf_init(out_buf: *uv_buf_t, base: *u8, len: size_t); + fn rust_uv_last_error(loop_handle: *c_void) -> uv_err_t; + // FIXME ref #2064 + fn rust_uv_strerror(err: *uv_err_t) -> *c_char; + // FIXME ref #2064 + fn rust_uv_err_name(err: *uv_err_t) -> *c_char; + fn rust_uv_ip4_addrp(ip: *u8, port: c_int) -> *sockaddr_in; + fn rust_uv_ip6_addrp(ip: *u8, port: c_int) -> *sockaddr_in6; + fn rust_uv_free_ip4_addr(addr: *sockaddr_in); + fn rust_uv_free_ip6_addr(addr: *sockaddr_in6); + fn rust_uv_ip4_name(src: *sockaddr_in, dst: *u8, size: size_t) -> c_int; + fn rust_uv_ip6_name(src: *sockaddr_in6, dst: *u8, size: size_t) -> c_int; + fn rust_uv_ip4_port(src: *sockaddr_in) -> c_uint; + fn rust_uv_ip6_port(src: *sockaddr_in6) -> c_uint; + // FIXME ref #2064 + fn rust_uv_tcp_connect(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + after_cb: *u8, + addr: *sockaddr_in) -> c_int; + // FIXME ref #2064 + fn rust_uv_tcp_bind(tcp_server: *uv_tcp_t, addr: *sockaddr_in) -> c_int; + // FIXME ref #2064 + fn rust_uv_tcp_connect6(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + after_cb: *u8, + addr: *sockaddr_in6) -> c_int; + // FIXME ref #2064 + fn rust_uv_tcp_bind6(tcp_server: *uv_tcp_t, addr: *sockaddr_in6) -> c_int; + fn rust_uv_tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, + name: *sockaddr_in) -> c_int; + fn rust_uv_tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, + name: *sockaddr_in6) ->c_int; + fn rust_uv_listen(stream: *c_void, backlog: c_int, cb: *u8) -> c_int; + fn rust_uv_accept(server: *c_void, client: *c_void) -> c_int; + fn rust_uv_write(req: *c_void, + stream: *c_void, + buf_in: *uv_buf_t, + buf_cnt: c_int, + cb: *u8) -> c_int; + fn rust_uv_read_start(stream: *c_void, + on_alloc: *u8, + on_read: *u8) -> c_int; + fn rust_uv_read_stop(stream: *c_void) -> c_int; + fn rust_uv_timer_init(loop_handle: *c_void, + timer_handle: *uv_timer_t) -> c_int; + fn rust_uv_timer_start(timer_handle: *uv_timer_t, + cb: *u8, + timeout: c_uint, + repeat: c_uint) -> c_int; + fn rust_uv_timer_stop(handle: *uv_timer_t) -> c_int; + + fn rust_uv_malloc_buf_base_of(sug_size: size_t) -> *u8; + fn rust_uv_free_base_of_buf(buf: uv_buf_t); + fn rust_uv_get_stream_handle_from_connect_req(connect_req: *uv_connect_t) -> *uv_stream_t; + fn rust_uv_get_stream_handle_from_write_req(write_req: *uv_write_t) -> *uv_stream_t; + fn rust_uv_get_loop_for_uv_handle(handle: *c_void) -> *c_void; + fn rust_uv_get_data_for_uv_loop(loop_ptr: *c_void) -> *c_void; + fn rust_uv_set_data_for_uv_loop(loop_ptr: *c_void, data: *c_void); + fn rust_uv_get_data_for_uv_handle(handle: *c_void) -> *c_void; + fn rust_uv_set_data_for_uv_handle(handle: *c_void, data: *c_void); + fn rust_uv_get_data_for_req(req: *c_void) -> *c_void; + fn rust_uv_set_data_for_req(req: *c_void, data: *c_void); + fn rust_uv_get_base_from_buf(buf: uv_buf_t) -> *u8; + fn rust_uv_get_len_from_buf(buf: uv_buf_t) -> size_t; +} diff --git a/src/libstd/rt/work_queue.rs b/src/libstd/rt/work_queue.rs new file mode 100644 index 00000000000..e9eb663392b --- /dev/null +++ b/src/libstd/rt/work_queue.rs @@ -0,0 +1,67 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 container::Container; +use option::*; +use vec::OwnedVector; +use unstable::sync::{Exclusive, exclusive}; +use cell::Cell; +use kinds::Owned; +use clone::Clone; + +pub struct WorkQueue<T> { + // XXX: Another mystery bug fixed by boxing this lock + priv queue: ~Exclusive<~[T]> +} + +pub impl<T: Owned> WorkQueue<T> { + fn new() -> WorkQueue<T> { + WorkQueue { + queue: ~exclusive(~[]) + } + } + + fn push(&mut self, value: T) { + let value = Cell(value); + self.queue.with(|q| q.unshift(value.take()) ); + } + + fn pop(&mut self) -> Option<T> { + do self.queue.with |q| { + if !q.is_empty() { + Some(q.shift()) + } else { + None + } + } + } + + fn steal(&mut self) -> Option<T> { + do self.queue.with |q| { + if !q.is_empty() { + Some(q.pop()) + } else { + None + } + } + } + + fn is_empty(&self) -> bool { + self.queue.with_imm(|q| q.is_empty() ) + } +} + +impl<T> Clone for WorkQueue<T> { + fn clone(&self) -> WorkQueue<T> { + WorkQueue { + queue: self.queue.clone() + } + } +} diff --git a/src/libstd/run.rs b/src/libstd/run.rs new file mode 100644 index 00000000000..02757ab4899 --- /dev/null +++ b/src/libstd/run.rs @@ -0,0 +1,856 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Process spawning +use cast; +use io; +use libc; +use libc::{pid_t, c_void, c_int}; +use comm::{stream, SharedChan, GenericChan, GenericPort}; +use option::{Some, None}; +use os; +use prelude::*; +use ptr; +use str; +use task; +use vec; + +/// A value representing a child process +pub struct Program { + priv pid: pid_t, + priv handle: *(), + priv in_fd: c_int, + priv out_file: *libc::FILE, + priv err_file: *libc::FILE, + priv finished: bool, +} + +impl Drop for Program { + fn finalize(&self) { + // FIXME #4943: transmute is bad. + let mut_self: &mut Program = unsafe { cast::transmute(self) }; + + mut_self.finish(); + mut_self.close_outputs(); + free_handle(self.handle); + } +} + +pub impl Program { + + /// Returns the process id of the program + fn get_id(&mut self) -> pid_t { self.pid } + + /// Returns an io::Writer that can be used to write to stdin + fn input(&mut self) -> @io::Writer { + io::fd_writer(self.in_fd, false) + } + + /// Returns an io::Reader that can be used to read from stdout + fn output(&mut self) -> @io::Reader { + io::FILE_reader(self.out_file, false) + } + + /// Returns an io::Reader that can be used to read from stderr + fn err(&mut self) -> @io::Reader { + io::FILE_reader(self.err_file, false) + } + + /// Closes the handle to the child processes standard input + fn close_input(&mut self) { + let invalid_fd = -1i32; + if self.in_fd != invalid_fd { + unsafe { + libc::close(self.in_fd); + } + self.in_fd = invalid_fd; + } + } + + priv fn close_outputs(&mut self) { + unsafe { + fclose_and_null(&mut self.out_file); + fclose_and_null(&mut self.err_file); + } + } + + /** + * Waits for the child process to terminate. Closes the handle + * to stdin if necessary. + */ + fn finish(&mut self) -> int { + if self.finished { return 0; } + self.finished = true; + self.close_input(); + return waitpid(self.pid); + } + + priv fn destroy_internal(&mut self, force: bool) { + killpid(self.pid, force); + self.finish(); + self.close_outputs(); + + #[cfg(windows)] + fn killpid(pid: pid_t, _force: bool) { + unsafe { + libc::funcs::extra::kernel32::TerminateProcess( + cast::transmute(pid), 1); + } + } + + #[cfg(unix)] + fn killpid(pid: pid_t, force: bool) { + let signal = if force { + libc::consts::os::posix88::SIGKILL + } else { + libc::consts::os::posix88::SIGTERM + }; + + unsafe { + libc::funcs::posix88::signal::kill(pid, signal as c_int); + } + } + } + + /** + * Terminate the program, giving it a chance to clean itself up if + * this is supported by the operating system. + * + * On Posix OSs SIGTERM will be sent to the process. On Win32 + * TerminateProcess(..) will be called. + */ + fn destroy(&mut self) { self.destroy_internal(false); } + + /** + * Terminate the program as soon as possible without giving it a + * chance to clean itself up. + * + * On Posix OSs SIGKILL will be sent to the process. On Win32 + * TerminateProcess(..) will be called. + */ + fn force_destroy(&mut self) { self.destroy_internal(true); } +} + + +/** + * Run a program, providing stdin, stdout and stderr handles + * + * # Arguments + * + * * prog - The path to an executable + * * args - Vector of arguments to pass to the child process + * * env - optional env-modification for child + * * dir - optional dir to run child in (default current dir) + * * in_fd - A file descriptor for the child to use as std input + * * out_fd - A file descriptor for the child to use as std output + * * err_fd - A file descriptor for the child to use as std error + * + * # Return value + * + * The process id of the spawned process + */ +pub fn spawn_process(prog: &str, args: &[~str], + env: &Option<~[(~str,~str)]>, + dir: &Option<~str>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> pid_t { + + let res = spawn_process_internal(prog, args, env, dir, in_fd, out_fd, err_fd); + free_handle(res.handle); + return res.pid; +} + +struct RunProgramResult { + // the process id of the program (this should never be negative) + pid: pid_t, + // a handle to the process - on unix this will always be NULL, but on windows it will be a + // HANDLE to the process, which will prevent the pid being re-used until the handle is closed. + handle: *(), +} + +#[cfg(windows)] +fn spawn_process_internal(prog: &str, args: &[~str], + env: &Option<~[(~str,~str)]>, + dir: &Option<~str>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult { + + use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; + use libc::consts::os::extra::{ + TRUE, FALSE, + STARTF_USESTDHANDLES, + INVALID_HANDLE_VALUE, + DUPLICATE_SAME_ACCESS + }; + use libc::funcs::extra::kernel32::{ + GetCurrentProcess, + DuplicateHandle, + CloseHandle, + CreateProcessA + }; + use libc::funcs::extra::msvcrt::get_osfhandle; + + unsafe { + + let mut si = zeroed_startupinfo(); + si.cb = sys::size_of::<STARTUPINFO>() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; + + let cur_proc = GetCurrentProcess(); + + let orig_std_in = get_osfhandle(if in_fd > 0 { in_fd } else { 0 }) as HANDLE; + if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: %s", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: %s", os::last_os_error()); + } + + let orig_std_out = get_osfhandle(if out_fd > 0 { out_fd } else { 1 }) as HANDLE; + if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: %s", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: %s", os::last_os_error()); + } + + let orig_std_err = get_osfhandle(if err_fd > 0 { err_fd } else { 2 }) as HANDLE; + if orig_std_err as HANDLE == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: %s", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: %s", os::last_os_error()); + } + + let cmd = make_command_line(prog, args); + let mut pi = zeroed_process_information(); + let mut create_err = None; + + do with_envp(env) |envp| { + do with_dirp(dir) |dirp| { + do str::as_c_str(cmd) |cmdp| { + let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), + ptr::mut_null(), ptr::mut_null(), TRUE, + 0, envp, dirp, &mut si, &mut pi); + if created == FALSE { + create_err = Some(os::last_os_error()); + } + } + } + } + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + for create_err.each |msg| { + fail!("failure in CreateProcess: %s", *msg); + } + + // We close the thread handle because we don't care about keeping the thread id valid, + // and we aren't keeping the thread handle around to be able to close it later. We don't + // close the process handle however because we want the process id to stay valid at least + // until the calling code closes the process handle. + CloseHandle(pi.hThread); + + RunProgramResult { + pid: pi.dwProcessId as pid_t, + handle: pi.hProcess as *() + } + } +} + +#[cfg(windows)] +fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { + libc::types::os::arch::extra::STARTUPINFO { + cb: 0, + lpReserved: ptr::mut_null(), + lpDesktop: ptr::mut_null(), + lpTitle: ptr::mut_null(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::mut_null(), + hStdInput: ptr::mut_null(), + hStdOutput: ptr::mut_null(), + hStdError: ptr::mut_null() + } +} + +#[cfg(windows)] +fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { + libc::types::os::arch::extra::PROCESS_INFORMATION { + hProcess: ptr::mut_null(), + hThread: ptr::mut_null(), + dwProcessId: 0, + dwThreadId: 0 + } +} + +// FIXME: this is only pub so it can be tested (see issue #4536) +#[cfg(windows)] +pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { + + let mut cmd = ~""; + append_arg(&mut cmd, prog); + for args.each |arg| { + cmd.push_char(' '); + append_arg(&mut cmd, *arg); + } + return cmd; + + fn append_arg(cmd: &mut ~str, arg: &str) { + let quote = arg.any(|c| c == ' ' || c == '\t'); + if quote { + cmd.push_char('"'); + } + for uint::range(0, arg.len()) |i| { + append_char_at(cmd, arg, i); + } + if quote { + cmd.push_char('"'); + } + } + + fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { + match arg[i] as char { + '"' => { + // Escape quotes. + cmd.push_str("\\\""); + } + '\\' => { + if backslash_run_ends_in_quote(arg, i) { + // Double all backslashes that are in runs before quotes. + cmd.push_str("\\\\"); + } else { + // Pass other backslashes through unescaped. + cmd.push_char('\\'); + } + } + c => { + cmd.push_char(c); + } + } + } + + fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { + while i < s.len() && s[i] as char == '\\' { + i += 1; + } + return i < s.len() && s[i] as char == '"'; + } +} + +#[cfg(unix)] +fn spawn_process_internal(prog: &str, args: &[~str], + env: &Option<~[(~str,~str)]>, + dir: &Option<~str>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult { + + use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; + use libc::funcs::bsd44::getdtablesize; + + mod rustrt { + use libc::c_void; + + #[abi = "cdecl"] + pub extern { + unsafe fn rust_unset_sigprocmask(); + unsafe fn rust_set_environ(envp: *c_void); + } + } + + unsafe { + + let pid = fork(); + if pid < 0 { + fail!("failure in fork: %s", os::last_os_error()); + } else if pid > 0 { + return RunProgramResult {pid: pid, handle: ptr::null()}; + } + + rustrt::rust_unset_sigprocmask(); + + if in_fd > 0 && dup2(in_fd, 0) == -1 { + fail!("failure in dup2(in_fd, 0): %s", os::last_os_error()); + } + if out_fd > 0 && dup2(out_fd, 1) == -1 { + fail!("failure in dup2(out_fd, 1): %s", os::last_os_error()); + } + if err_fd > 0 && dup2(err_fd, 2) == -1 { + fail!("failure in dup3(err_fd, 2): %s", os::last_os_error()); + } + // close all other fds + for int::range_rev(getdtablesize() as int - 1, 2) |fd| { + close(fd as c_int); + } + + for dir.each |dir| { + do str::as_c_str(*dir) |dirp| { + if chdir(dirp) == -1 { + fail!("failure in chdir: %s", os::last_os_error()); + } + } + } + + do with_envp(env) |envp| { + if !envp.is_null() { + rustrt::rust_set_environ(envp); + } + do with_argv(prog, args) |argv| { + execvp(*argv, argv); + // execvp only returns if an error occurred + fail!("failure in execvp: %s", os::last_os_error()); + } + } + } +} + +#[cfg(unix)] +fn with_argv<T>(prog: &str, args: &[~str], + cb: &fn(**libc::c_char) -> T) -> T { + let mut argptrs = str::as_c_str(prog, |b| ~[b]); + let mut tmps = ~[]; + for args.each |arg| { + let t = @copy *arg; + tmps.push(t); + argptrs.push_all(str::as_c_str(*t, |b| ~[b])); + } + argptrs.push(ptr::null()); + vec::as_imm_buf(argptrs, |buf, _len| cb(buf)) +} + +#[cfg(unix)] +fn with_envp<T>(env: &Option<~[(~str,~str)]>, + cb: &fn(*c_void) -> T) -> T { + // On posixy systems we can pass a char** for envp, which is + // a null-terminated array of "k=v\n" strings. + match *env { + Some(ref es) if !vec::is_empty(*es) => { + let mut tmps = ~[]; + let mut ptrs = ~[]; + + for (*es).each |e| { + let (k,v) = copy *e; + let t = @(fmt!("%s=%s", k, v)); + tmps.push(t); + ptrs.push_all(str::as_c_str(*t, |b| ~[b])); + } + ptrs.push(ptr::null()); + vec::as_imm_buf(ptrs, |p, _len| + unsafe { cb(::cast::transmute(p)) } + ) + } + _ => cb(ptr::null()) + } +} + +#[cfg(windows)] +fn with_envp<T>(env: &Option<~[(~str,~str)]>, + cb: &fn(*mut c_void) -> T) -> T { + // On win32 we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + unsafe { + match *env { + Some(ref es) if !vec::is_empty(*es) => { + let mut blk : ~[u8] = ~[]; + for (*es).each |e| { + let (k,v) = copy *e; + let t = fmt!("%s=%s", k, v); + let mut v : ~[u8] = ::cast::transmute(t); + blk += v; + ::cast::forget(v); + } + blk += ~[0_u8]; + vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p))) + } + _ => cb(ptr::mut_null()) + } + } +} + +#[cfg(windows)] +fn with_dirp<T>(d: &Option<~str>, + cb: &fn(*libc::c_char) -> T) -> T { + match *d { + Some(ref dir) => str::as_c_str(*dir, cb), + None => cb(ptr::null()) + } +} + +/// helper function that closes non-NULL files and then makes them NULL +priv unsafe fn fclose_and_null(f: &mut *libc::FILE) { + if *f != 0 as *libc::FILE { + libc::fclose(*f); + *f = 0 as *libc::FILE; + } +} + +#[cfg(windows)] +priv fn free_handle(handle: *()) { + unsafe { + libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle)); + } +} + +#[cfg(unix)] +priv fn free_handle(_handle: *()) { + // unix has no process handle object, just a pid +} + +/** + * Spawns a process and waits for it to terminate + * + * # Arguments + * + * * prog - The path to an executable + * * args - Vector of arguments to pass to the child process + * + * # Return value + * + * The process's exit code + */ +pub fn run_program(prog: &str, args: &[~str]) -> int { + let res = spawn_process_internal(prog, args, &None, &None, + 0i32, 0i32, 0i32); + let code = waitpid(res.pid); + free_handle(res.handle); + return code; +} + +/** + * Spawns a process and returns a Program + * + * The returned value is a <Program> object that can be used for sending and + * receiving data over the standard file descriptors. The class will ensure + * that file descriptors are closed properly. + * + * # Arguments + * + * * prog - The path to an executable + * * args - Vector of arguments to pass to the child process + * + * # Return value + * + * A <Program> object + */ +pub fn start_program(prog: &str, args: &[~str]) -> Program { + let pipe_input = os::pipe(); + let pipe_output = os::pipe(); + let pipe_err = os::pipe(); + let res = + spawn_process_internal(prog, args, &None, &None, + pipe_input.in, pipe_output.out, + pipe_err.out); + + unsafe { + libc::close(pipe_input.in); + libc::close(pipe_output.out); + libc::close(pipe_err.out); + } + + Program { + pid: res.pid, + handle: res.handle, + in_fd: pipe_input.out, + out_file: os::fdopen(pipe_output.in), + err_file: os::fdopen(pipe_err.in), + finished: false, + } +} + +fn read_all(rd: @io::Reader) -> ~str { + let buf = io::with_bytes_writer(|wr| { + let mut bytes = [0, ..4096]; + while !rd.eof() { + let nread = rd.read(bytes, bytes.len()); + wr.write(bytes.slice(0, nread)); + } + }); + str::from_bytes(buf) +} + +pub struct ProgramOutput {status: int, out: ~str, err: ~str} + +/** + * Spawns a process, waits for it to exit, and returns the exit code, and + * contents of stdout and stderr. + * + * # Arguments + * + * * prog - The path to an executable + * * args - Vector of arguments to pass to the child process + * + * # Return value + * + * A record, {status: int, out: str, err: str} containing the exit code, + * the contents of stdout and the contents of stderr. + */ +pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput { + let pipe_in = os::pipe(); + let pipe_out = os::pipe(); + let pipe_err = os::pipe(); + let res = spawn_process_internal(prog, args, &None, &None, + pipe_in.in, pipe_out.out, pipe_err.out); + + os::close(pipe_in.in); + os::close(pipe_out.out); + os::close(pipe_err.out); + os::close(pipe_in.out); + + // Spawn two entire schedulers to read both stdout and sterr + // in parallel so we don't deadlock while blocking on one + // or the other. FIXME (#2625): Surely there's a much more + // clever way to do this. + let (p, ch) = stream(); + let ch = SharedChan::new(ch); + let ch_clone = ch.clone(); + do task::spawn_sched(task::SingleThreaded) { + let errput = readclose(pipe_err.in); + ch.send((2, errput)); + }; + do task::spawn_sched(task::SingleThreaded) { + let output = readclose(pipe_out.in); + ch_clone.send((1, output)); + }; + + let status = waitpid(res.pid); + free_handle(res.handle); + + let mut errs = ~""; + let mut outs = ~""; + let mut count = 2; + while count > 0 { + let stream = p.recv(); + match stream { + (1, copy s) => { + outs = s; + } + (2, copy s) => { + errs = s; + } + (n, _) => { + fail!("program_output received an unexpected file number: %u", n); + } + }; + count -= 1; + }; + return ProgramOutput {status: status, + out: outs, + err: errs}; +} + +pub fn writeclose(fd: c_int, s: ~str) { + use io::WriterUtil; + + error!("writeclose %d, %s", fd as int, s); + let writer = io::fd_writer(fd, false); + writer.write_str(s); + + os::close(fd); +} + +pub fn readclose(fd: c_int) -> ~str { + unsafe { + let file = os::fdopen(fd); + let reader = io::FILE_reader(file, false); + let buf = io::with_bytes_writer(|writer| { + let mut bytes = [0, ..4096]; + while !reader.eof() { + let nread = reader.read(bytes, bytes.len()); + writer.write(bytes.slice(0, nread)); + } + }); + os::fclose(file); + str::from_bytes(buf) + } +} + +/** + * Waits for a process to exit and returns the exit code, failing + * if there is no process with the specified id. + */ +pub fn waitpid(pid: pid_t) -> int { + return waitpid_os(pid); + + #[cfg(windows)] + fn waitpid_os(pid: pid_t) -> int { + + use libc::types::os::arch::extra::DWORD; + use libc::consts::os::extra::{ + SYNCHRONIZE, + PROCESS_QUERY_INFORMATION, + FALSE, + STILL_ACTIVE, + INFINITE, + WAIT_FAILED + }; + use libc::funcs::extra::kernel32::{ + OpenProcess, + GetExitCodeProcess, + CloseHandle, + WaitForSingleObject + }; + + unsafe { + + let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD); + if proc.is_null() { + fail!("failure in OpenProcess: %s", os::last_os_error()); + } + + loop { + let mut status = 0; + if GetExitCodeProcess(proc, &mut status) == FALSE { + CloseHandle(proc); + fail!("failure in GetExitCodeProcess: %s", os::last_os_error()); + } + if status != STILL_ACTIVE { + CloseHandle(proc); + return status as int; + } + if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED { + CloseHandle(proc); + fail!("failure in WaitForSingleObject: %s", os::last_os_error()); + } + } + } + } + + #[cfg(unix)] + fn waitpid_os(pid: pid_t) -> int { + + use libc::funcs::posix01::wait::*; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WIFEXITED(status: i32) -> bool { + (status & 0xffi32) == 0i32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WIFEXITED(status: i32) -> bool { + (status & 0x7fi32) == 0i32 + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8i32) & 0xffi32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WEXITSTATUS(status: i32) -> i32 { + status >> 8i32 + } + + let mut status = 0 as c_int; + if unsafe { waitpid(pid, &mut status, 0) } == -1 { + fail!("failure in waitpid: %s", os::last_os_error()); + } + + return if WIFEXITED(status) { + WEXITSTATUS(status) as int + } else { + 1 + }; + } +} + +#[cfg(test)] +mod tests { + use option::None; + use os; + use run::{readclose, writeclose}; + use run; + + #[test] + #[cfg(windows)] + fn test_make_command_line() { + assert_eq!( + run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), + ~"prog aaa bbb ccc" + ); + assert_eq!( + run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), + ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), + ~"\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!( + run::make_command_line("echo", [~"a b c"]), + ~"echo \"a b c\"" + ); + } + + // Regression test for memory leaks + #[test] + fn test_leaks() { + run::run_program("echo", []); + run::start_program("echo", []); + run::program_output("echo", []); + } + + #[test] + #[allow(non_implicitly_copyable_typarams)] + fn test_pipes() { + let pipe_in = os::pipe(); + let pipe_out = os::pipe(); + let pipe_err = os::pipe(); + + let pid = + run::spawn_process( + "cat", [], &None, &None, + pipe_in.in, pipe_out.out, pipe_err.out); + os::close(pipe_in.in); + os::close(pipe_out.out); + os::close(pipe_err.out); + + if pid == -1i32 { fail!(); } + let expected = ~"test"; + writeclose(pipe_in.out, copy expected); + let actual = readclose(pipe_out.in); + readclose(pipe_err.in); + run::waitpid(pid); + + debug!(copy expected); + debug!(copy actual); + assert_eq!(expected, actual); + } + + #[test] + fn waitpid() { + let pid = run::spawn_process("false", [], + &None, &None, + 0i32, 0i32, 0i32); + let status = run::waitpid(pid); + assert_eq!(status, 1); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn waitpid_non_existant_pid() { + run::waitpid(123456789); // assume that this pid doesn't exist + } +} diff --git a/src/libstd/semver.rs b/src/libstd/semver.rs deleted file mode 100644 index 09855c9334e..00000000000 --- a/src/libstd/semver.rs +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Semver parsing and logic - -use core::char; -use core::cmp; -use core::io::{ReaderUtil}; -use core::io; -use core::option::{Option, Some, None}; -use core::str; -use core::to_str::ToStr; -use core::uint; - -#[deriving(Eq)] -pub enum Identifier { - Numeric(uint), - AlphaNumeric(~str) -} - -impl cmp::Ord for Identifier { - #[inline(always)] - fn lt(&self, other: &Identifier) -> bool { - match (self, other) { - (&Numeric(a), &Numeric(b)) => a < b, - (&Numeric(_), _) => true, - (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b, - (&AlphaNumeric(_), _) => false - } - } - #[inline(always)] - fn le(&self, other: &Identifier) -> bool { - ! (other < self) - } - #[inline(always)] - fn gt(&self, other: &Identifier) -> bool { - other < self - } - #[inline(always)] - fn ge(&self, other: &Identifier) -> bool { - ! (self < other) - } -} - -impl ToStr for Identifier { - #[inline(always)] - fn to_str(&self) -> ~str { - match self { - &Numeric(n) => n.to_str(), - &AlphaNumeric(ref s) => s.to_str() - } - } -} - - -#[deriving(Eq)] -pub struct Version { - major: uint, - minor: uint, - patch: uint, - pre: ~[Identifier], - build: ~[Identifier], -} - -impl ToStr for Version { - #[inline(always)] - fn to_str(&self) -> ~str { - let s = fmt!("%u.%u.%u", self.major, self.minor, self.patch); - let s = if self.pre.is_empty() { - s - } else { - s + "-" + str::connect(self.pre.map(|i| i.to_str()), ".") - }; - if self.build.is_empty() { - s - } else { - s + "+" + str::connect(self.build.map(|i| i.to_str()), ".") - } - } -} - -impl cmp::Ord for Version { - #[inline(always)] - fn lt(&self, other: &Version) -> bool { - - self.major < other.major || - - (self.major == other.major && - self.minor < other.minor) || - - (self.major == other.major && - self.minor == other.minor && - self.patch < other.patch) || - - (self.major == other.major && - self.minor == other.minor && - self.patch == other.patch && - // NB: semver spec says 0.0.0-pre < 0.0.0 - // but the version of ord defined for vec - // says that [] < [pre], so we alter it - // here. - (match (self.pre.len(), other.pre.len()) { - (0, 0) => false, - (0, _) => false, - (_, 0) => true, - (_, _) => self.pre < other.pre - })) || - - (self.major == other.major && - self.minor == other.minor && - self.patch == other.patch && - self.pre == other.pre && - self.build < other.build) - } - - #[inline(always)] - fn le(&self, other: &Version) -> bool { - ! (other < self) - } - #[inline(always)] - fn gt(&self, other: &Version) -> bool { - other < self - } - #[inline(always)] - fn ge(&self, other: &Version) -> bool { - ! (self < other) - } -} - -condition! { - bad_parse: () -> (); -} - -fn take_nonempty_prefix(rdr: @io::Reader, - ch: char, - pred: &fn(char) -> bool) -> (~str, char) { - let mut buf = ~""; - let mut ch = ch; - while pred(ch) { - str::push_char(&mut buf, ch); - ch = rdr.read_char(); - } - if buf.is_empty() { - bad_parse::cond.raise(()) - } - debug!("extracted nonempty prefix: %s", buf); - (buf, ch) -} - -fn take_num(rdr: @io::Reader, ch: char) -> (uint, char) { - let (s, ch) = take_nonempty_prefix(rdr, ch, char::is_digit); - match uint::from_str(s) { - None => { bad_parse::cond.raise(()); (0, ch) }, - Some(i) => (i, ch) - } -} - -fn take_ident(rdr: @io::Reader, ch: char) -> (Identifier, char) { - let (s,ch) = take_nonempty_prefix(rdr, ch, char::is_alphanumeric); - if s.all(char::is_digit) { - match uint::from_str(s) { - None => { bad_parse::cond.raise(()); (Numeric(0), ch) }, - Some(i) => (Numeric(i), ch) - } - } else { - (AlphaNumeric(s), ch) - } -} - -fn expect(ch: char, c: char) { - if ch != c { - bad_parse::cond.raise(()) - } -} - -fn parse_reader(rdr: @io::Reader) -> Version { - let (major, ch) = take_num(rdr, rdr.read_char()); - expect(ch, '.'); - let (minor, ch) = take_num(rdr, rdr.read_char()); - expect(ch, '.'); - let (patch, ch) = take_num(rdr, rdr.read_char()); - - let mut pre = ~[]; - let mut build = ~[]; - - let mut ch = ch; - if ch == '-' { - loop { - let (id, c) = take_ident(rdr, rdr.read_char()); - pre.push(id); - ch = c; - if ch != '.' { break; } - } - } - - if ch == '+' { - loop { - let (id, c) = take_ident(rdr, rdr.read_char()); - build.push(id); - ch = c; - if ch != '.' { break; } - } - } - - Version { - major: major, - minor: minor, - patch: patch, - pre: pre, - build: build, - } -} - - -pub fn parse(s: &str) -> Option<Version> { - if !s.is_ascii() { - return None; - } - let s = s.trim(); - let mut bad = false; - do bad_parse::cond.trap(|_| { debug!("bad"); bad = true }).in { - do io::with_str_reader(s) |rdr| { - let v = parse_reader(rdr); - if bad || v.to_str() != s.to_owned() { - None - } else { - Some(v) - } - } - } -} - -#[test] -fn test_parse() { - assert_eq!(parse(""), None); - assert_eq!(parse(" "), None); - assert_eq!(parse("1"), None); - assert_eq!(parse("1.2"), None); - assert_eq!(parse("1.2"), None); - assert_eq!(parse("1"), None); - assert_eq!(parse("1.2"), None); - assert_eq!(parse("1.2.3-"), None); - assert_eq!(parse("a.b.c"), None); - assert_eq!(parse("1.2.3 abc"), None); - - assert!(parse("1.2.3") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[], - build: ~[], - })); - assert!(parse(" 1.2.3 ") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[], - build: ~[], - })); - assert!(parse("1.2.3-alpha1") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[AlphaNumeric(~"alpha1")], - build: ~[] - })); - assert!(parse(" 1.2.3-alpha1 ") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[AlphaNumeric(~"alpha1")], - build: ~[] - })); - assert!(parse("1.2.3+build5") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[], - build: ~[AlphaNumeric(~"build5")] - })); - assert!(parse(" 1.2.3+build5 ") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[], - build: ~[AlphaNumeric(~"build5")] - })); - assert!(parse("1.2.3-alpha1+build5") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[AlphaNumeric(~"alpha1")], - build: ~[AlphaNumeric(~"build5")] - })); - assert!(parse(" 1.2.3-alpha1+build5 ") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[AlphaNumeric(~"alpha1")], - build: ~[AlphaNumeric(~"build5")] - })); - assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf ") == Some(Version { - major: 1u, - minor: 2u, - patch: 3u, - pre: ~[Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)], - build: ~[AlphaNumeric(~"build5"), - Numeric(7), - AlphaNumeric(~"3aedf")] - })); - -} - -#[test] -fn test_eq() { - assert_eq!(parse("1.2.3"), parse("1.2.3")); - assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1")); -} - -#[test] -fn test_ne() { - assert!(parse("0.0.0") != parse("0.0.1")); - assert!(parse("0.0.0") != parse("0.1.0")); - assert!(parse("0.0.0") != parse("1.0.0")); - assert!(parse("1.2.3-alpha") != parse("1.2.3-beta")); -} - -#[test] -fn test_lt() { - assert!(parse("0.0.0") < parse("1.2.3-alpha2")); - assert!(parse("1.0.0") < parse("1.2.3-alpha2")); - assert!(parse("1.2.0") < parse("1.2.3-alpha2")); - assert!(parse("1.2.3-alpha1") < parse("1.2.3")); - assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2")); - assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2"))); -} - -#[test] -fn test_le() { - assert!(parse("0.0.0") <= parse("1.2.3-alpha2")); - assert!(parse("1.0.0") <= parse("1.2.3-alpha2")); - assert!(parse("1.2.0") <= parse("1.2.3-alpha2")); - assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2")); - assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2")); -} - -#[test] -fn test_gt() { - assert!(parse("1.2.3-alpha2") > parse("0.0.0")); - assert!(parse("1.2.3-alpha2") > parse("1.0.0")); - assert!(parse("1.2.3-alpha2") > parse("1.2.0")); - assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1")); - assert!(parse("1.2.3") > parse("1.2.3-alpha2")); - assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2"))); -} - -#[test] -fn test_ge() { - assert!(parse("1.2.3-alpha2") >= parse("0.0.0")); - assert!(parse("1.2.3-alpha2") >= parse("1.0.0")); - assert!(parse("1.2.3-alpha2") >= parse("1.2.0")); - assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1")); - assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2")); -} - -#[test] -fn test_spec_order() { - - let vs = ["1.0.0-alpha", - "1.0.0-alpha.1", - "1.0.0-beta.2", - "1.0.0-beta.11", - "1.0.0-rc.1", - "1.0.0-rc.1+build.1", - "1.0.0", - "1.0.0+0.3.7", - "1.3.7+build", - "1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a"]; - let mut i = 1; - while i < vs.len() { - let a = parse(vs[i-1]).get(); - let b = parse(vs[i]).get(); - assert!(a < b); - i += 1; - } -} diff --git a/src/libstd/serialize.rs b/src/libstd/serialize.rs deleted file mode 100644 index c270f46751f..00000000000 --- a/src/libstd/serialize.rs +++ /dev/null @@ -1,907 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Support code for encoding and decoding types. - -/* -Core encoding and decoding interfaces. -*/ - -#[forbid(non_camel_case_types)]; - -use core::hashmap::{HashMap, HashSet}; -use core::trie::{TrieMap, TrieSet}; -use deque::Deque; -use dlist::DList; -use treemap::{TreeMap, TreeSet}; - -pub trait Encoder { - // Primitive types: - fn emit_nil(&mut self); - fn emit_uint(&mut self, v: uint); - fn emit_u64(&mut self, v: u64); - fn emit_u32(&mut self, v: u32); - fn emit_u16(&mut self, v: u16); - fn emit_u8(&mut self, v: u8); - fn emit_int(&mut self, v: int); - fn emit_i64(&mut self, v: i64); - fn emit_i32(&mut self, v: i32); - fn emit_i16(&mut self, v: i16); - fn emit_i8(&mut self, v: i8); - fn emit_bool(&mut self, v: bool); - fn emit_float(&mut self, v: float); - fn emit_f64(&mut self, v: f64); - fn emit_f32(&mut self, v: f32); - fn emit_char(&mut self, v: char); - fn emit_str(&mut self, v: &str); - - // Compound types: - fn emit_enum(&mut self, name: &str, f: &fn(&mut Self)); - - fn emit_enum_variant(&mut self, - v_name: &str, - v_id: uint, - len: uint, - f: &fn(&mut Self)); - fn emit_enum_variant_arg(&mut self, a_idx: uint, f: &fn(&mut Self)); - - fn emit_enum_struct_variant(&mut self, - v_name: &str, - v_id: uint, - len: uint, - f: &fn(&mut Self)); - fn emit_enum_struct_variant_field(&mut self, - f_name: &str, - f_idx: uint, - f: &fn(&mut Self)); - - fn emit_struct(&mut self, name: &str, len: uint, f: &fn(&mut Self)); - fn emit_struct_field(&mut self, - f_name: &str, - f_idx: uint, - f: &fn(&mut Self)); - - fn emit_tuple(&mut self, len: uint, f: &fn(&mut Self)); - fn emit_tuple_arg(&mut self, idx: uint, f: &fn(&mut Self)); - - fn emit_tuple_struct(&mut self, name: &str, len: uint, f: &fn(&mut Self)); - fn emit_tuple_struct_arg(&mut self, f_idx: uint, f: &fn(&mut Self)); - - // Specialized types: - fn emit_option(&mut self, f: &fn(&mut Self)); - fn emit_option_none(&mut self); - fn emit_option_some(&mut self, f: &fn(&mut Self)); - - fn emit_seq(&mut self, len: uint, f: &fn(this: &mut Self)); - fn emit_seq_elt(&mut self, idx: uint, f: &fn(this: &mut Self)); - - fn emit_map(&mut self, len: uint, f: &fn(&mut Self)); - fn emit_map_elt_key(&mut self, idx: uint, f: &fn(&mut Self)); - fn emit_map_elt_val(&mut self, idx: uint, f: &fn(&mut Self)); -} - -pub trait Decoder { - // Primitive types: - fn read_nil(&mut self) -> (); - fn read_uint(&mut self) -> uint; - fn read_u64(&mut self) -> u64; - fn read_u32(&mut self) -> u32; - fn read_u16(&mut self) -> u16; - fn read_u8(&mut self) -> u8; - fn read_int(&mut self) -> int; - fn read_i64(&mut self) -> i64; - fn read_i32(&mut self) -> i32; - fn read_i16(&mut self) -> i16; - fn read_i8(&mut self) -> i8; - fn read_bool(&mut self) -> bool; - fn read_f64(&mut self) -> f64; - fn read_f32(&mut self) -> f32; - fn read_float(&mut self) -> float; - fn read_char(&mut self) -> char; - fn read_str(&mut self) -> ~str; - - // Compound types: - fn read_enum<T>(&mut self, name: &str, f: &fn(&mut Self) -> T) -> T; - - fn read_enum_variant<T>(&mut self, - names: &[&str], - f: &fn(&mut Self, uint) -> T) - -> T; - fn read_enum_variant_arg<T>(&mut self, - a_idx: uint, - f: &fn(&mut Self) -> T) - -> T; - - fn read_enum_struct_variant<T>(&mut self, - names: &[&str], - f: &fn(&mut Self, uint) -> T) - -> T; - fn read_enum_struct_variant_field<T>(&mut self, - &f_name: &str, - f_idx: uint, - f: &fn(&mut Self) -> T) - -> T; - - fn read_struct<T>(&mut self, - s_name: &str, - len: uint, - f: &fn(&mut Self) -> T) - -> T; - fn read_struct_field<T>(&mut self, - f_name: &str, - f_idx: uint, - f: &fn(&mut Self) -> T) - -> T; - - fn read_tuple<T>(&mut self, f: &fn(&mut Self, uint) -> T) -> T; - fn read_tuple_arg<T>(&mut self, a_idx: uint, f: &fn(&mut Self) -> T) -> T; - - fn read_tuple_struct<T>(&mut self, - s_name: &str, - f: &fn(&mut Self, uint) -> T) - -> T; - fn read_tuple_struct_arg<T>(&mut self, - a_idx: uint, - f: &fn(&mut Self) -> T) - -> T; - - // Specialized types: - fn read_option<T>(&mut self, f: &fn(&mut Self, bool) -> T) -> T; - - fn read_seq<T>(&mut self, f: &fn(&mut Self, uint) -> T) -> T; - fn read_seq_elt<T>(&mut self, idx: uint, f: &fn(&mut Self) -> T) -> T; - - fn read_map<T>(&mut self, f: &fn(&mut Self, uint) -> T) -> T; - fn read_map_elt_key<T>(&mut self, idx: uint, f: &fn(&mut Self) -> T) -> T; - fn read_map_elt_val<T>(&mut self, idx: uint, f: &fn(&mut Self) -> T) -> T; -} - -pub trait Encodable<S:Encoder> { - fn encode(&self, s: &mut S); -} - -pub trait Decodable<D:Decoder> { - fn decode(d: &mut D) -> Self; -} - -impl<S:Encoder> Encodable<S> for uint { - fn encode(&self, s: &mut S) { - s.emit_uint(*self) - } -} - -impl<D:Decoder> Decodable<D> for uint { - fn decode(d: &mut D) -> uint { - d.read_uint() - } -} - -impl<S:Encoder> Encodable<S> for u8 { - fn encode(&self, s: &mut S) { - s.emit_u8(*self) - } -} - -impl<D:Decoder> Decodable<D> for u8 { - fn decode(d: &mut D) -> u8 { - d.read_u8() - } -} - -impl<S:Encoder> Encodable<S> for u16 { - fn encode(&self, s: &mut S) { - s.emit_u16(*self) - } -} - -impl<D:Decoder> Decodable<D> for u16 { - fn decode(d: &mut D) -> u16 { - d.read_u16() - } -} - -impl<S:Encoder> Encodable<S> for u32 { - fn encode(&self, s: &mut S) { - s.emit_u32(*self) - } -} - -impl<D:Decoder> Decodable<D> for u32 { - fn decode(d: &mut D) -> u32 { - d.read_u32() - } -} - -impl<S:Encoder> Encodable<S> for u64 { - fn encode(&self, s: &mut S) { - s.emit_u64(*self) - } -} - -impl<D:Decoder> Decodable<D> for u64 { - fn decode(d: &mut D) -> u64 { - d.read_u64() - } -} - -impl<S:Encoder> Encodable<S> for int { - fn encode(&self, s: &mut S) { - s.emit_int(*self) - } -} - -impl<D:Decoder> Decodable<D> for int { - fn decode(d: &mut D) -> int { - d.read_int() - } -} - -impl<S:Encoder> Encodable<S> for i8 { - fn encode(&self, s: &mut S) { - s.emit_i8(*self) - } -} - -impl<D:Decoder> Decodable<D> for i8 { - fn decode(d: &mut D) -> i8 { - d.read_i8() - } -} - -impl<S:Encoder> Encodable<S> for i16 { - fn encode(&self, s: &mut S) { - s.emit_i16(*self) - } -} - -impl<D:Decoder> Decodable<D> for i16 { - fn decode(d: &mut D) -> i16 { - d.read_i16() - } -} - -impl<S:Encoder> Encodable<S> for i32 { - fn encode(&self, s: &mut S) { - s.emit_i32(*self) - } -} - -impl<D:Decoder> Decodable<D> for i32 { - fn decode(d: &mut D) -> i32 { - d.read_i32() - } -} - -impl<S:Encoder> Encodable<S> for i64 { - fn encode(&self, s: &mut S) { - s.emit_i64(*self) - } -} - -impl<D:Decoder> Decodable<D> for i64 { - fn decode(d: &mut D) -> i64 { - d.read_i64() - } -} - -impl<'self, S:Encoder> Encodable<S> for &'self str { - fn encode(&self, s: &mut S) { - s.emit_str(*self) - } -} - -impl<S:Encoder> Encodable<S> for ~str { - fn encode(&self, s: &mut S) { - s.emit_str(*self) - } -} - -impl<D:Decoder> Decodable<D> for ~str { - fn decode(d: &mut D) -> ~str { - d.read_str() - } -} - -impl<S:Encoder> Encodable<S> for @str { - fn encode(&self, s: &mut S) { - s.emit_str(*self) - } -} - -impl<D:Decoder> Decodable<D> for @str { - fn decode(d: &mut D) -> @str { - d.read_str().to_managed() - } -} - -impl<S:Encoder> Encodable<S> for float { - fn encode(&self, s: &mut S) { - s.emit_float(*self) - } -} - -impl<D:Decoder> Decodable<D> for float { - fn decode(d: &mut D) -> float { - d.read_float() - } -} - -impl<S:Encoder> Encodable<S> for f32 { - fn encode(&self, s: &mut S) { - s.emit_f32(*self) - } -} - -impl<D:Decoder> Decodable<D> for f32 { - fn decode(d: &mut D) -> f32 { - d.read_f32() - } -} - -impl<S:Encoder> Encodable<S> for f64 { - fn encode(&self, s: &mut S) { - s.emit_f64(*self) - } -} - -impl<D:Decoder> Decodable<D> for f64 { - fn decode(d: &mut D) -> f64 { - d.read_f64() - } -} - -impl<S:Encoder> Encodable<S> for bool { - fn encode(&self, s: &mut S) { - s.emit_bool(*self) - } -} - -impl<D:Decoder> Decodable<D> for bool { - fn decode(d: &mut D) -> bool { - d.read_bool() - } -} - -impl<S:Encoder> Encodable<S> for () { - fn encode(&self, s: &mut S) { - s.emit_nil() - } -} - -impl<D:Decoder> Decodable<D> for () { - fn decode(d: &mut D) -> () { - d.read_nil() - } -} - -impl<'self, S:Encoder,T:Encodable<S>> Encodable<S> for &'self T { - fn encode(&self, s: &mut S) { - (**self).encode(s) - } -} - -impl<S:Encoder,T:Encodable<S>> Encodable<S> for ~T { - fn encode(&self, s: &mut S) { - (**self).encode(s) - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for ~T { - fn decode(d: &mut D) -> ~T { - ~Decodable::decode(d) - } -} - -impl<S:Encoder,T:Encodable<S>> Encodable<S> for @T { - fn encode(&self, s: &mut S) { - (**self).encode(s) - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for @T { - fn decode(d: &mut D) -> @T { - @Decodable::decode(d) - } -} - -impl<'self, S:Encoder,T:Encodable<S>> Encodable<S> for &'self [T] { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - for self.eachi |i, e| { - s.emit_seq_elt(i, |s| e.encode(s)) - } - } - } -} - -impl<S:Encoder,T:Encodable<S>> Encodable<S> for ~[T] { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - for self.eachi |i, e| { - s.emit_seq_elt(i, |s| e.encode(s)) - } - } - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for ~[T] { - fn decode(d: &mut D) -> ~[T] { - do d.read_seq |d, len| { - do vec::from_fn(len) |i| { - d.read_seq_elt(i, |d| Decodable::decode(d)) - } - } - } -} - -impl<S:Encoder,T:Encodable<S>> Encodable<S> for @[T] { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - for self.eachi |i, e| { - s.emit_seq_elt(i, |s| e.encode(s)) - } - } - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for @[T] { - fn decode(d: &mut D) -> @[T] { - do d.read_seq |d, len| { - do at_vec::from_fn(len) |i| { - d.read_seq_elt(i, |d| Decodable::decode(d)) - } - } - } -} - -impl<S:Encoder,T:Encodable<S>> Encodable<S> for Option<T> { - fn encode(&self, s: &mut S) { - do s.emit_option |s| { - match *self { - None => s.emit_option_none(), - Some(ref v) => s.emit_option_some(|s| v.encode(s)), - } - } - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for Option<T> { - fn decode(d: &mut D) -> Option<T> { - do d.read_option |d, b| { - if b { - Some(Decodable::decode(d)) - } else { - None - } - } - } -} - -impl<S:Encoder,T0:Encodable<S>,T1:Encodable<S>> Encodable<S> for (T0, T1) { - fn encode(&self, s: &mut S) { - match *self { - (ref t0, ref t1) => { - do s.emit_seq(2) |s| { - s.emit_seq_elt(0, |s| t0.encode(s)); - s.emit_seq_elt(1, |s| t1.encode(s)); - } - } - } - } -} - -impl<D:Decoder,T0:Decodable<D>,T1:Decodable<D>> Decodable<D> for (T0, T1) { - fn decode(d: &mut D) -> (T0, T1) { - do d.read_seq |d, len| { - assert_eq!(len, 2); - ( - d.read_seq_elt(0, |d| Decodable::decode(d)), - d.read_seq_elt(1, |d| Decodable::decode(d)) - ) - } - } -} - -impl< - S: Encoder, - T0: Encodable<S>, - T1: Encodable<S>, - T2: Encodable<S> -> Encodable<S> for (T0, T1, T2) { - fn encode(&self, s: &mut S) { - match *self { - (ref t0, ref t1, ref t2) => { - do s.emit_seq(3) |s| { - s.emit_seq_elt(0, |s| t0.encode(s)); - s.emit_seq_elt(1, |s| t1.encode(s)); - s.emit_seq_elt(2, |s| t2.encode(s)); - } - } - } - } -} - -impl< - D: Decoder, - T0: Decodable<D>, - T1: Decodable<D>, - T2: Decodable<D> -> Decodable<D> for (T0, T1, T2) { - fn decode(d: &mut D) -> (T0, T1, T2) { - do d.read_seq |d, len| { - assert_eq!(len, 3); - ( - d.read_seq_elt(0, |d| Decodable::decode(d)), - d.read_seq_elt(1, |d| Decodable::decode(d)), - d.read_seq_elt(2, |d| Decodable::decode(d)) - ) - } - } -} - -impl< - S: Encoder, - T0: Encodable<S>, - T1: Encodable<S>, - T2: Encodable<S>, - T3: Encodable<S> -> Encodable<S> for (T0, T1, T2, T3) { - fn encode(&self, s: &mut S) { - match *self { - (ref t0, ref t1, ref t2, ref t3) => { - do s.emit_seq(4) |s| { - s.emit_seq_elt(0, |s| t0.encode(s)); - s.emit_seq_elt(1, |s| t1.encode(s)); - s.emit_seq_elt(2, |s| t2.encode(s)); - s.emit_seq_elt(3, |s| t3.encode(s)); - } - } - } - } -} - -impl< - D: Decoder, - T0: Decodable<D>, - T1: Decodable<D>, - T2: Decodable<D>, - T3: Decodable<D> -> Decodable<D> for (T0, T1, T2, T3) { - fn decode(d: &mut D) -> (T0, T1, T2, T3) { - do d.read_seq |d, len| { - assert_eq!(len, 4); - ( - d.read_seq_elt(0, |d| Decodable::decode(d)), - d.read_seq_elt(1, |d| Decodable::decode(d)), - d.read_seq_elt(2, |d| Decodable::decode(d)), - d.read_seq_elt(3, |d| Decodable::decode(d)) - ) - } - } -} - -impl< - S: Encoder, - T0: Encodable<S>, - T1: Encodable<S>, - T2: Encodable<S>, - T3: Encodable<S>, - T4: Encodable<S> -> Encodable<S> for (T0, T1, T2, T3, T4) { - fn encode(&self, s: &mut S) { - match *self { - (ref t0, ref t1, ref t2, ref t3, ref t4) => { - do s.emit_seq(5) |s| { - s.emit_seq_elt(0, |s| t0.encode(s)); - s.emit_seq_elt(1, |s| t1.encode(s)); - s.emit_seq_elt(2, |s| t2.encode(s)); - s.emit_seq_elt(3, |s| t3.encode(s)); - s.emit_seq_elt(4, |s| t4.encode(s)); - } - } - } - } -} - -impl< - D: Decoder, - T0: Decodable<D>, - T1: Decodable<D>, - T2: Decodable<D>, - T3: Decodable<D>, - T4: Decodable<D> -> Decodable<D> for (T0, T1, T2, T3, T4) { - fn decode(d: &mut D) -> (T0, T1, T2, T3, T4) { - do d.read_seq |d, len| { - assert_eq!(len, 5); - ( - d.read_seq_elt(0, |d| Decodable::decode(d)), - d.read_seq_elt(1, |d| Decodable::decode(d)), - d.read_seq_elt(2, |d| Decodable::decode(d)), - d.read_seq_elt(3, |d| Decodable::decode(d)), - d.read_seq_elt(4, |d| Decodable::decode(d)) - ) - } - } -} - -impl< - S: Encoder, - T: Encodable<S> + Copy -> Encodable<S> for @mut DList<T> { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.size) |s| { - let mut i = 0; - for self.each |e| { - s.emit_seq_elt(i, |s| e.encode(s)); - i += 1; - } - } - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for @mut DList<T> { - fn decode(d: &mut D) -> @mut DList<T> { - let list = DList(); - do d.read_seq |d, len| { - for uint::range(0, len) |i| { - list.push(d.read_seq_elt(i, |d| Decodable::decode(d))); - } - } - list - } -} - -impl< - S: Encoder, - T: Encodable<S> -> Encodable<S> for Deque<T> { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - for self.eachi |i, e| { - s.emit_seq_elt(i, |s| e.encode(s)); - } - } - } -} - -impl<D:Decoder,T:Decodable<D>> Decodable<D> for Deque<T> { - fn decode(d: &mut D) -> Deque<T> { - let mut deque = Deque::new(); - do d.read_seq |d, len| { - for uint::range(0, len) |i| { - deque.add_back(d.read_seq_elt(i, |d| Decodable::decode(d))); - } - } - deque - } -} - -impl< - E: Encoder, - K: Encodable<E> + Hash + IterBytes + Eq, - V: Encodable<E> -> Encodable<E> for HashMap<K, V> { - fn encode(&self, e: &mut E) { - do e.emit_map(self.len()) |e| { - let mut i = 0; - for self.each |key, val| { - e.emit_map_elt_key(i, |e| key.encode(e)); - e.emit_map_elt_val(i, |e| val.encode(e)); - i += 1; - } - } - } -} - -impl< - D: Decoder, - K: Decodable<D> + Hash + IterBytes + Eq, - V: Decodable<D> -> Decodable<D> for HashMap<K, V> { - fn decode(d: &mut D) -> HashMap<K, V> { - do d.read_map |d, len| { - let mut map = HashMap::with_capacity(len); - for uint::range(0, len) |i| { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d)); - let val = d.read_map_elt_val(i, |d| Decodable::decode(d)); - map.insert(key, val); - } - map - } - } -} - -impl< - S: Encoder, - T: Encodable<S> + Hash + IterBytes + Eq -> Encodable<S> for HashSet<T> { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - let mut i = 0; - for self.each |e| { - s.emit_seq_elt(i, |s| e.encode(s)); - i += 1; - } - } - } -} - -impl< - D: Decoder, - T: Decodable<D> + Hash + IterBytes + Eq -> Decodable<D> for HashSet<T> { - fn decode(d: &mut D) -> HashSet<T> { - do d.read_seq |d, len| { - let mut set = HashSet::with_capacity(len); - for uint::range(0, len) |i| { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))); - } - set - } - } -} - -impl< - E: Encoder, - V: Encodable<E> -> Encodable<E> for TrieMap<V> { - fn encode(&self, e: &mut E) { - do e.emit_map(self.len()) |e| { - let mut i = 0; - for self.each |key, val| { - e.emit_map_elt_key(i, |e| key.encode(e)); - e.emit_map_elt_val(i, |e| val.encode(e)); - i += 1; - } - } - } -} - -impl< - D: Decoder, - V: Decodable<D> -> Decodable<D> for TrieMap<V> { - fn decode(d: &mut D) -> TrieMap<V> { - do d.read_map |d, len| { - let mut map = TrieMap::new(); - for uint::range(0, len) |i| { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d)); - let val = d.read_map_elt_val(i, |d| Decodable::decode(d)); - map.insert(key, val); - } - map - } - } -} - -impl<S: Encoder> Encodable<S> for TrieSet { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - let mut i = 0; - for self.each |e| { - s.emit_seq_elt(i, |s| e.encode(s)); - i += 1; - } - } - } -} - -impl<D: Decoder> Decodable<D> for TrieSet { - fn decode(d: &mut D) -> TrieSet { - do d.read_seq |d, len| { - let mut set = TrieSet::new(); - for uint::range(0, len) |i| { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))); - } - set - } - } -} - -impl< - E: Encoder, - K: Encodable<E> + Eq + TotalOrd, - V: Encodable<E> + Eq -> Encodable<E> for TreeMap<K, V> { - fn encode(&self, e: &mut E) { - do e.emit_map(self.len()) |e| { - let mut i = 0; - for self.each |key, val| { - e.emit_map_elt_key(i, |e| key.encode(e)); - e.emit_map_elt_val(i, |e| val.encode(e)); - i += 1; - } - } - } -} - -impl< - D: Decoder, - K: Decodable<D> + Eq + TotalOrd, - V: Decodable<D> + Eq -> Decodable<D> for TreeMap<K, V> { - fn decode(d: &mut D) -> TreeMap<K, V> { - do d.read_map |d, len| { - let mut map = TreeMap::new(); - for uint::range(0, len) |i| { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d)); - let val = d.read_map_elt_val(i, |d| Decodable::decode(d)); - map.insert(key, val); - } - map - } - } -} - -impl< - S: Encoder, - T: Encodable<S> + Eq + TotalOrd -> Encodable<S> for TreeSet<T> { - fn encode(&self, s: &mut S) { - do s.emit_seq(self.len()) |s| { - let mut i = 0; - for self.each |e| { - s.emit_seq_elt(i, |s| e.encode(s)); - i += 1; - } - } - } -} - -impl< - D: Decoder, - T: Decodable<D> + Eq + TotalOrd -> Decodable<D> for TreeSet<T> { - fn decode(d: &mut D) -> TreeSet<T> { - do d.read_seq |d, len| { - let mut set = TreeSet::new(); - for uint::range(0, len) |i| { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))); - } - set - } - } -} - -// ___________________________________________________________________________ -// Helper routines -// -// In some cases, these should eventually be coded as traits. - -pub trait EncoderHelpers { - fn emit_from_vec<T>(&mut self, v: &[T], f: &fn(&mut Self, v: &T)); -} - -impl<S:Encoder> EncoderHelpers for S { - fn emit_from_vec<T>(&mut self, v: &[T], f: &fn(&mut S, &T)) { - do self.emit_seq(v.len()) |this| { - for v.eachi |i, e| { - do this.emit_seq_elt(i) |this| { - f(this, e) - } - } - } - } -} - -pub trait DecoderHelpers { - fn read_to_vec<T>(&mut self, f: &fn(&mut Self) -> T) -> ~[T]; -} - -impl<D:Decoder> DecoderHelpers for D { - fn read_to_vec<T>(&mut self, f: &fn(&mut D) -> T) -> ~[T] { - do self.read_seq |this, len| { - do vec::from_fn(len) |i| { - this.read_seq_elt(i, |this| f(this)) - } - } - } -} diff --git a/src/libstd/sha1.rs b/src/libstd/sha1.rs deleted file mode 100644 index d7051d73469..00000000000 --- a/src/libstd/sha1.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * An implementation of the SHA-1 cryptographic hash. - * - * First create a `sha1` object using the `sha1` constructor, then - * feed it input using the `input` or `input_str` methods, which may be - * called any number of times. - * - * After the entire input has been fed to the hash read the result using - * the `result` or `result_str` methods. - * - * The `sha1` object may be reused to create multiple hashes by calling - * the `reset` method. - */ - -use core::str; -use core::uint; -use core::vec; - -/* - * A SHA-1 implementation derived from Paul E. Jones's reference - * implementation, which is written for clarity, not speed. At some - * point this will want to be rewritten. - */ - -/// The SHA-1 interface -trait Sha1 { - /// Provide message input as bytes - fn input(&mut self, &const [u8]); - /// Provide message input as string - fn input_str(&mut self, &str); - /** - * Read the digest as a vector of 20 bytes. After calling this no further - * input may be provided until reset is called. - */ - fn result(&mut self) -> ~[u8]; - /** - * Read the digest as a hex string. After calling this no further - * input may be provided until reset is called. - */ - fn result_str(&mut self) -> ~str; - /// Reset the SHA-1 state for reuse - fn reset(&mut self); -} - -// Some unexported constants -static digest_buf_len: uint = 5u; -static msg_block_len: uint = 64u; -static work_buf_len: uint = 80u; -static k0: u32 = 0x5A827999u32; -static k1: u32 = 0x6ED9EBA1u32; -static k2: u32 = 0x8F1BBCDCu32; -static k3: u32 = 0xCA62C1D6u32; - - -/// Construct a `sha` object -pub fn sha1() -> @Sha1 { - struct Sha1State - { h: ~[u32], - len_low: u32, - len_high: u32, - msg_block: ~[u8], - msg_block_idx: uint, - computed: bool, - work_buf: @mut ~[u32]}; - - fn add_input(st: &mut Sha1State, msg: &const [u8]) { - assert!((!st.computed)); - for vec::each_const(msg) |element| { - st.msg_block[st.msg_block_idx] = *element; - st.msg_block_idx += 1u; - st.len_low += 8u32; - if st.len_low == 0u32 { - st.len_high += 1u32; - if st.len_high == 0u32 { - // FIXME: Need better failure mode (#2346) - fail!(); - } - } - if st.msg_block_idx == msg_block_len { process_msg_block(st); } - } - } - fn process_msg_block(st: &mut Sha1State) { - assert_eq!(st.h.len(), digest_buf_len); - assert_eq!(vec::uniq_len(st.work_buf), work_buf_len); - let mut t: int; // Loop counter - let w = st.work_buf; - - // Initialize the first 16 words of the vector w - t = 0; - while t < 16 { - let mut tmp; - tmp = (st.msg_block[t * 4] as u32) << 24u32; - tmp = tmp | (st.msg_block[t * 4 + 1] as u32) << 16u32; - tmp = tmp | (st.msg_block[t * 4 + 2] as u32) << 8u32; - tmp = tmp | (st.msg_block[t * 4 + 3] as u32); - w[t] = tmp; - t += 1; - } - - // Initialize the rest of vector w - while t < 80 { - let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; - w[t] = circular_shift(1u32, val); - t += 1; - } - let mut a = st.h[0]; - let mut b = st.h[1]; - let mut c = st.h[2]; - let mut d = st.h[3]; - let mut e = st.h[4]; - let mut temp: u32; - t = 0; - while t < 20 { - temp = circular_shift(5u32, a) + (b & c | !b & d) + e + w[t] + k0; - e = d; - d = c; - c = circular_shift(30u32, b); - b = a; - a = temp; - t += 1; - } - while t < 40 { - temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k1; - e = d; - d = c; - c = circular_shift(30u32, b); - b = a; - a = temp; - t += 1; - } - while t < 60 { - temp = - circular_shift(5u32, a) + (b & c | b & d | c & d) + e + w[t] + - k2; - e = d; - d = c; - c = circular_shift(30u32, b); - b = a; - a = temp; - t += 1; - } - while t < 80 { - temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k3; - e = d; - d = c; - c = circular_shift(30u32, b); - b = a; - a = temp; - t += 1; - } - st.h[0] = st.h[0] + a; - st.h[1] = st.h[1] + b; - st.h[2] = st.h[2] + c; - st.h[3] = st.h[3] + d; - st.h[4] = st.h[4] + e; - st.msg_block_idx = 0u; - } - fn circular_shift(bits: u32, word: u32) -> u32 { - return word << bits | word >> 32u32 - bits; - } - fn mk_result(st: &mut Sha1State) -> ~[u8] { - if !(*st).computed { pad_msg(st); (*st).computed = true; } - let mut rs: ~[u8] = ~[]; - for vec::each_mut((*st).h) |ptr_hpart| { - let hpart = *ptr_hpart; - let a = (hpart >> 24u32 & 0xFFu32) as u8; - let b = (hpart >> 16u32 & 0xFFu32) as u8; - let c = (hpart >> 8u32 & 0xFFu32) as u8; - let d = (hpart & 0xFFu32) as u8; - rs = vec::append(copy rs, [a, b, c, d]); - } - return rs; - } - - /* - * According to the standard, the message must be padded to an even - * 512 bits. The first padding bit must be a '1'. The last 64 bits - * represent the length of the original message. All bits in between - * should be 0. This function will pad the message according to those - * rules by filling the msg_block vector accordingly. It will also - * call process_msg_block() appropriately. When it returns, it - * can be assumed that the message digest has been computed. - */ - fn pad_msg(st: &mut Sha1State) { - assert_eq!(vec::len((*st).msg_block), msg_block_len); - - /* - * Check to see if the current message block is too small to hold - * the initial padding bits and length. If so, we will pad the - * block, process it, and then continue padding into a second block. - */ - if (*st).msg_block_idx > 55u { - (*st).msg_block[(*st).msg_block_idx] = 0x80u8; - (*st).msg_block_idx += 1u; - while (*st).msg_block_idx < msg_block_len { - (*st).msg_block[(*st).msg_block_idx] = 0u8; - (*st).msg_block_idx += 1u; - } - process_msg_block(st); - } else { - (*st).msg_block[(*st).msg_block_idx] = 0x80u8; - (*st).msg_block_idx += 1u; - } - while (*st).msg_block_idx < 56u { - (*st).msg_block[(*st).msg_block_idx] = 0u8; - (*st).msg_block_idx += 1u; - } - - // Store the message length as the last 8 octets - (*st).msg_block[56] = ((*st).len_high >> 24u32 & 0xFFu32) as u8; - (*st).msg_block[57] = ((*st).len_high >> 16u32 & 0xFFu32) as u8; - (*st).msg_block[58] = ((*st).len_high >> 8u32 & 0xFFu32) as u8; - (*st).msg_block[59] = ((*st).len_high & 0xFFu32) as u8; - (*st).msg_block[60] = ((*st).len_low >> 24u32 & 0xFFu32) as u8; - (*st).msg_block[61] = ((*st).len_low >> 16u32 & 0xFFu32) as u8; - (*st).msg_block[62] = ((*st).len_low >> 8u32 & 0xFFu32) as u8; - (*st).msg_block[63] = ((*st).len_low & 0xFFu32) as u8; - process_msg_block(st); - } - - impl Sha1 for Sha1State { - fn reset(&mut self) { - assert_eq!(self.h.len(), digest_buf_len); - self.len_low = 0u32; - self.len_high = 0u32; - self.msg_block_idx = 0u; - self.h[0] = 0x67452301u32; - self.h[1] = 0xEFCDAB89u32; - self.h[2] = 0x98BADCFEu32; - self.h[3] = 0x10325476u32; - self.h[4] = 0xC3D2E1F0u32; - self.computed = false; - } - fn input(&mut self, msg: &const [u8]) { add_input(self, msg); } - fn input_str(&mut self, msg: &str) { - let bs = str::to_bytes(msg); - add_input(self, bs); - } - fn result(&mut self) -> ~[u8] { return mk_result(self); } - fn result_str(&mut self) -> ~str { - let rr = mk_result(self); - let mut s = ~""; - for rr.each |b| { - let hex = uint::to_str_radix(*b as uint, 16u); - if hex.len() == 1 { - s += "0"; - } - s += hex; - } - return s; - } - } - let st = Sha1State { - h: vec::from_elem(digest_buf_len, 0u32), - len_low: 0u32, - len_high: 0u32, - msg_block: vec::from_elem(msg_block_len, 0u8), - msg_block_idx: 0u, - computed: false, - work_buf: @mut vec::from_elem(work_buf_len, 0u32) - }; - let mut sh = @st as @Sha1; - sh.reset(); - return sh; -} - -#[cfg(test)] -mod tests { - use sha1; - - use core::str; - use core::vec; - - #[test] - fn test() { - struct Test { - input: ~str, - output: ~[u8], - output_str: ~str, - } - - fn a_million_letter_a() -> ~str { - let mut i = 0; - let mut rs = ~""; - while i < 100000 { - str::push_str(&mut rs, ~"aaaaaaaaaa"); - i += 1; - } - return rs; - } - // Test messages from FIPS 180-1 - - let fips_180_1_tests = ~[ - Test { - input: ~"abc", - output: ~[ - 0xA9u8, 0x99u8, 0x3Eu8, 0x36u8, - 0x47u8, 0x06u8, 0x81u8, 0x6Au8, - 0xBAu8, 0x3Eu8, 0x25u8, 0x71u8, - 0x78u8, 0x50u8, 0xC2u8, 0x6Cu8, - 0x9Cu8, 0xD0u8, 0xD8u8, 0x9Du8, - ], - output_str: ~"a9993e364706816aba3e25717850c26c9cd0d89d" - }, - Test { - input: - ~"abcdbcdecdefdefgefghfghighij" + - ~"hijkijkljklmklmnlmnomnopnopq", - output: ~[ - 0x84u8, 0x98u8, 0x3Eu8, 0x44u8, - 0x1Cu8, 0x3Bu8, 0xD2u8, 0x6Eu8, - 0xBAu8, 0xAEu8, 0x4Au8, 0xA1u8, - 0xF9u8, 0x51u8, 0x29u8, 0xE5u8, - 0xE5u8, 0x46u8, 0x70u8, 0xF1u8, - ], - output_str: ~"84983e441c3bd26ebaae4aa1f95129e5e54670f1" - }, - Test { - input: a_million_letter_a(), - output: ~[ - 0x34u8, 0xAAu8, 0x97u8, 0x3Cu8, - 0xD4u8, 0xC4u8, 0xDAu8, 0xA4u8, - 0xF6u8, 0x1Eu8, 0xEBu8, 0x2Bu8, - 0xDBu8, 0xADu8, 0x27u8, 0x31u8, - 0x65u8, 0x34u8, 0x01u8, 0x6Fu8, - ], - output_str: ~"34aa973cd4c4daa4f61eeb2bdbad27316534016f" - }, - ]; - // Examples from wikipedia - - let wikipedia_tests = ~[ - Test { - input: ~"The quick brown fox jumps over the lazy dog", - output: ~[ - 0x2fu8, 0xd4u8, 0xe1u8, 0xc6u8, - 0x7au8, 0x2du8, 0x28u8, 0xfcu8, - 0xedu8, 0x84u8, 0x9eu8, 0xe1u8, - 0xbbu8, 0x76u8, 0xe7u8, 0x39u8, - 0x1bu8, 0x93u8, 0xebu8, 0x12u8, - ], - output_str: ~"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - }, - Test { - input: ~"The quick brown fox jumps over the lazy cog", - output: ~[ - 0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8, - 0xd2u8, 0x5eu8, 0x1bu8, 0x3au8, - 0xfau8, 0xd3u8, 0xe8u8, 0x5au8, - 0x0bu8, 0xd1u8, 0x7du8, 0x9bu8, - 0x10u8, 0x0du8, 0xb4u8, 0xb3u8, - ], - output_str: ~"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3", - }, - ]; - let tests = fips_180_1_tests + wikipedia_tests; - fn check_vec_eq(v0: ~[u8], v1: ~[u8]) { - assert_eq!(vec::len::<u8>(v0), vec::len::<u8>(v1)); - let len = vec::len::<u8>(v0); - let mut i = 0u; - while i < len { - let a = v0[i]; - let b = v1[i]; - assert_eq!(a, b); - i += 1u; - } - } - // Test that it works when accepting the message all at once - - let mut sh = sha1::sha1(); - for tests.each |t| { - sh.input_str(t.input); - let out = sh.result(); - check_vec_eq(copy t.output, out); - - let out_str = sh.result_str(); - assert_eq!(out_str.len(), 40); - assert!(out_str == t.output_str); - - sh.reset(); - } - - - // Test that it works when accepting the message in pieces - for tests.each |t| { - let len = str::len(t.input); - let mut left = len; - while left > 0u { - let take = (left + 1u) / 2u; - sh.input_str(str::slice(t.input, len - left, - take + len - left).to_owned()); - left = left - take; - } - let out = sh.result(); - check_vec_eq(copy t.output, out); - - let out_str = sh.result_str(); - assert_eq!(out_str.len(), 40); - assert!(out_str == t.output_str); - - sh.reset(); - } - } -} diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs deleted file mode 100644 index aa8372bab78..00000000000 --- a/src/libstd/smallintmap.rs +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * A simple map based on a vector for small integer keys. Space requirements - * are O(highest integer key). - */ - -use core::container::{Container, Mutable, Map, Set}; -use core::old_iter::BaseIter; -use core::old_iter; -use core::option::{Some, None}; -use core::util::replace; - -pub struct SmallIntMap<T> { - priv v: ~[Option<T>], -} - -impl<V> Container for SmallIntMap<V> { - /// Return the number of elements in the map - fn len(&const self) -> uint { - let mut sz = 0; - for uint::range(0, vec::uniq_len(&const self.v)) |i| { - match self.v[i] { - Some(_) => sz += 1, - None => {} - } - } - sz - } - - /// Return true if the map contains no elements - fn is_empty(&const self) -> bool { self.len() == 0 } -} - -impl<V> Mutable for SmallIntMap<V> { - /// Clear the map, removing all key-value pairs. - fn clear(&mut self) { self.v.clear() } -} - -impl<V> Map<uint, V> for SmallIntMap<V> { - /// Return true if the map contains a value for the specified key - fn contains_key(&self, key: &uint) -> bool { - self.find(key).is_some() - } - - /// Visit all key-value pairs in order - fn each<'a>(&'a self, it: &fn(&uint, &'a V) -> bool) -> bool { - for uint::range(0, self.v.len()) |i| { - match self.v[i] { - Some(ref elt) => if !it(&i, elt) { return false; }, - None => () - } - } - return true; - } - - /// Visit all keys in order - fn each_key(&self, blk: &fn(key: &uint) -> bool) -> bool { - self.each(|k, _| blk(k)) - } - - /// Visit all values in order - fn each_value<'a>(&'a self, blk: &fn(value: &'a V) -> bool) -> bool { - self.each(|_, v| blk(v)) - } - - /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, it: &fn(&uint, &mut V) -> bool) -> bool { - for uint::range(0, self.v.len()) |i| { - match self.v[i] { - Some(ref mut elt) => if !it(&i, elt) { return false; }, - None => () - } - } - return true; - } - - /// Return a reference to the value corresponding to the key - fn find<'a>(&'a self, key: &uint) -> Option<&'a V> { - if *key < self.v.len() { - match self.v[*key] { - Some(ref value) => Some(value), - None => None - } - } else { - None - } - } - - /// Return a mutable reference to the value corresponding to the key - fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut V> { - if *key < self.v.len() { - match self.v[*key] { - Some(ref mut value) => Some(value), - None => None - } - } else { - None - } - } - - /// Insert a key-value pair into the map. An existing value for a - /// key is replaced by the new value. Return true if the key did - /// not already exist in the map. - fn insert(&mut self, key: uint, value: V) -> bool { - let exists = self.contains_key(&key); - let len = self.v.len(); - if len <= key { - vec::grow_fn(&mut self.v, key - len + 1, |_| None); - } - self.v[key] = Some(value); - !exists - } - - /// Remove a key-value pair from the map. Return true if the key - /// was present in the map, otherwise false. - fn remove(&mut self, key: &uint) -> bool { - self.pop(key).is_some() - } - - /// Insert a key-value pair from the map. If the key already had a value - /// present in the map, that value is returned. Otherwise None is returned. - fn swap(&mut self, key: uint, value: V) -> Option<V> { - match self.find_mut(&key) { - Some(loc) => { return Some(replace(loc, value)); } - None => () - } - self.insert(key, value); - return None; - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - fn pop(&mut self, key: &uint) -> Option<V> { - if *key >= self.v.len() { - return None; - } - replace(&mut self.v[*key], None) - } -} - -pub impl<V> SmallIntMap<V> { - /// Create an empty SmallIntMap - fn new() -> SmallIntMap<V> { SmallIntMap{v: ~[]} } - - /// Visit all key-value pairs in reverse order - fn each_reverse<'a>(&'a self, it: &fn(uint, &'a V) -> bool) -> bool { - for uint::range_rev(self.v.len(), 0) |i| { - match self.v[i - 1] { - Some(ref elt) => if !it(i - 1, elt) { return false; }, - None => () - } - } - return true; - } - - fn get<'a>(&'a self, key: &uint) -> &'a V { - self.find(key).expect("key not present") - } -} - -pub impl<V:Copy> SmallIntMap<V> { - fn update_with_key(&mut self, key: uint, val: V, - ff: &fn(uint, V, V) -> V) -> bool { - let new_val = match self.find(&key) { - None => val, - Some(orig) => ff(key, *orig, val) - }; - self.insert(key, new_val) - } - - fn update(&mut self, key: uint, newval: V, ff: &fn(V, V) -> V) -> bool { - self.update_with_key(key, newval, |_k, v, v1| ff(v,v1)) - } -} - -pub struct SmallIntSet { - priv map: SmallIntMap<()> -} - -impl Container for SmallIntSet { - /// Return the number of elements in the map - fn len(&const self) -> uint { - self.map.len() - } - - /// Return true if the map contains no elements - fn is_empty(&const self) -> bool { self.len() == 0 } -} - -impl Mutable for SmallIntSet { - /// Clear the map, removing all key-value pairs. - fn clear(&mut self) { self.map.clear() } -} - -impl BaseIter<uint> for SmallIntSet { - /// Visit all values in order - fn each(&self, f: &fn(&uint) -> bool) -> bool { self.map.each_key(f) } - fn size_hint(&self) -> Option<uint> { Some(self.len()) } -} - -impl Set<uint> for SmallIntSet { - /// Return true if the set contains a value - fn contains(&self, value: &uint) -> bool { self.map.contains_key(value) } - - /// Add a value to the set. Return true if the value was not already - /// present in the set. - fn insert(&mut self, value: uint) -> bool { self.map.insert(value, ()) } - - /// Remove a value from the set. Return true if the value was - /// present in the set. - fn remove(&mut self, value: &uint) -> bool { self.map.remove(value) } - - /// Return true if the set has no elements in common with `other`. - /// This is equivalent to checking for an empty uintersection. - fn is_disjoint(&self, other: &SmallIntSet) -> bool { - old_iter::all(self, |v| !other.contains(v)) - } - - /// Return true if the set is a subset of another - fn is_subset(&self, other: &SmallIntSet) -> bool { - old_iter::all(self, |v| other.contains(v)) - } - - /// Return true if the set is a superset of another - fn is_superset(&self, other: &SmallIntSet) -> bool { - other.is_subset(self) - } - - /// Visit the values representing the difference - fn difference(&self, other: &SmallIntSet, f: &fn(&uint) -> bool) -> bool { - self.each(|v| other.contains(v) || f(v)) - } - - /// Visit the values representing the symmetric difference - fn symmetric_difference(&self, - other: &SmallIntSet, - f: &fn(&uint) -> bool) -> bool { - self.difference(other, f) && other.difference(self, f) - } - - /// Visit the values representing the uintersection - fn intersection(&self, other: &SmallIntSet, f: &fn(&uint) -> bool) -> bool { - self.each(|v| !other.contains(v) || f(v)) - } - - /// Visit the values representing the union - fn union(&self, other: &SmallIntSet, f: &fn(&uint) -> bool) -> bool { - self.each(f) && other.each(|v| self.contains(v) || f(v)) - } -} - -pub impl SmallIntSet { - /// Create an empty SmallIntSet - fn new() -> SmallIntSet { SmallIntSet{map: SmallIntMap::new()} } -} - -#[cfg(test)] -mod tests { - use super::SmallIntMap; - - #[test] - fn test_find_mut() { - let mut m = SmallIntMap::new(); - assert!(m.insert(1, 12)); - assert!(m.insert(2, 8)); - assert!(m.insert(5, 14)); - let new = 100; - match m.find_mut(&5) { - None => fail!(), Some(x) => *x = new - } - assert_eq!(m.find(&5), Some(&new)); - } - - #[test] - fn test_len() { - let mut map = SmallIntMap::new(); - assert_eq!(map.len(), 0); - assert!(map.is_empty()); - assert!(map.insert(5, 20)); - assert_eq!(map.len(), 1); - assert!(!map.is_empty()); - assert!(map.insert(11, 12)); - assert_eq!(map.len(), 2); - assert!(!map.is_empty()); - assert!(map.insert(14, 22)); - assert_eq!(map.len(), 3); - assert!(!map.is_empty()); - } - - #[test] - fn test_clear() { - let mut map = SmallIntMap::new(); - assert!(map.insert(5, 20)); - assert!(map.insert(11, 12)); - assert!(map.insert(14, 22)); - map.clear(); - assert!(map.is_empty()); - assert!(map.find(&5).is_none()); - assert!(map.find(&11).is_none()); - assert!(map.find(&14).is_none()); - } - - #[test] - fn test_insert_with_key() { - let mut map = SmallIntMap::new(); - - // given a new key, initialize it with this new count, given - // given an existing key, add more to its count - fn addMoreToCount(_k: uint, v0: uint, v1: uint) -> uint { - v0 + v1 - } - - fn addMoreToCount_simple(v0: uint, v1: uint) -> uint { - v0 + v1 - } - - // count integers - map.update(3, 1, addMoreToCount_simple); - map.update_with_key(9, 1, addMoreToCount); - map.update(3, 7, addMoreToCount_simple); - map.update_with_key(5, 3, addMoreToCount); - map.update_with_key(3, 2, addMoreToCount); - - // check the total counts - assert_eq!(map.find(&3).get(), &10); - assert_eq!(map.find(&5).get(), &3); - assert_eq!(map.find(&9).get(), &1); - - // sadly, no sevens were counted - assert!(map.find(&7).is_none()); - } - - #[test] - fn test_swap() { - let mut m = SmallIntMap::new(); - assert_eq!(m.swap(1, 2), None); - assert_eq!(m.swap(1, 3), Some(2)); - assert_eq!(m.swap(1, 4), Some(3)); - } - - #[test] - fn test_pop() { - let mut m = SmallIntMap::new(); - m.insert(1, 2); - assert_eq!(m.pop(&1), Some(2)); - assert_eq!(m.pop(&1), None); - } -} - -#[cfg(test)] -mod test_set { - use super::SmallIntSet; - - #[test] - fn test_disjoint() { - let mut xs = SmallIntSet::new(); - let mut ys = SmallIntSet::new(); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(5)); - assert!(ys.insert(11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(7)); - assert!(xs.insert(19)); - assert!(xs.insert(4)); - assert!(ys.insert(2)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(ys.insert(7)); - assert!(!xs.is_disjoint(&ys)); - assert!(!ys.is_disjoint(&xs)); - } - - #[test] - fn test_subset_and_superset() { - let mut a = SmallIntSet::new(); - assert!(a.insert(0)); - assert!(a.insert(5)); - assert!(a.insert(11)); - assert!(a.insert(7)); - - let mut b = SmallIntSet::new(); - assert!(b.insert(0)); - assert!(b.insert(7)); - assert!(b.insert(19)); - assert!(b.insert(250)); - assert!(b.insert(11)); - assert!(b.insert(200)); - - assert!(!a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(!b.is_superset(&a)); - - assert!(b.insert(5)); - - assert!(a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(b.is_superset(&a)); - } - - #[test] - fn test_intersection() { - let mut a = SmallIntSet::new(); - let mut b = SmallIntSet::new(); - - assert!(a.insert(11)); - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(77)); - assert!(a.insert(103)); - assert!(a.insert(5)); - - assert!(b.insert(2)); - assert!(b.insert(11)); - assert!(b.insert(77)); - assert!(b.insert(5)); - assert!(b.insert(3)); - - let mut i = 0; - let expected = [3, 5, 11, 77]; - for a.intersection(&b) |x| { - assert!(vec::contains(expected, x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_difference() { - let mut a = SmallIntSet::new(); - let mut b = SmallIntSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(3)); - assert!(b.insert(9)); - - let mut i = 0; - let expected = [1, 5, 11]; - for a.difference(&b) |x| { - assert!(vec::contains(expected, x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_symmetric_difference() { - let mut a = SmallIntSet::new(); - let mut b = SmallIntSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(3)); - assert!(b.insert(9)); - assert!(b.insert(14)); - assert!(b.insert(22)); - - let mut i = 0; - let expected = [1, 5, 11, 14, 22]; - for a.symmetric_difference(&b) |x| { - assert!(vec::contains(expected, x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_union() { - let mut a = SmallIntSet::new(); - let mut b = SmallIntSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - assert!(a.insert(16)); - assert!(a.insert(19)); - assert!(a.insert(24)); - - assert!(b.insert(1)); - assert!(b.insert(5)); - assert!(b.insert(9)); - assert!(b.insert(13)); - assert!(b.insert(19)); - - let mut i = 0; - let expected = [1, 3, 5, 9, 11, 13, 16, 19, 24]; - for a.union(&b) |x| { - assert!(vec::contains(expected, x)); - i += 1 - } - assert_eq!(i, expected.len()); - } -} diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs deleted file mode 100644 index d896fa8c096..00000000000 --- a/src/libstd/sort.rs +++ /dev/null @@ -1,1221 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Sorting methods - -use core::cmp::{Eq, Ord}; -use core::vec::len; -use core::vec; -use core::util::swap; - -type Le<'self, T> = &'self fn(v1: &T, v2: &T) -> bool; - -/** - * Merge sort. Returns a new vector containing the sorted list. - * - * Has worst case O(n log n) performance, best case O(n), but - * is not space efficient. This is a stable sort. - */ -pub fn merge_sort<T:Copy>(v: &[T], le: Le<T>) -> ~[T] { - type Slice = (uint, uint); - - return merge_sort_(v, (0u, len(v)), le); - - fn merge_sort_<T:Copy>(v: &[T], slice: Slice, le: Le<T>) - -> ~[T] { - let begin = slice.first(); - let end = slice.second(); - - let v_len = end - begin; - if v_len == 0 { return ~[]; } - if v_len == 1 { return ~[v[begin]]; } - - let mid = v_len / 2 + begin; - let a = (begin, mid); - let b = (mid, end); - return merge(le, merge_sort_(v, a, le), merge_sort_(v, b, le)); - } - - fn merge<T:Copy>(le: Le<T>, a: &[T], b: &[T]) -> ~[T] { - let mut rs = vec::with_capacity(len(a) + len(b)); - let a_len = len(a); - let mut a_ix = 0; - let b_len = len(b); - let mut b_ix = 0; - while a_ix < a_len && b_ix < b_len { - if le(&a[a_ix], &b[b_ix]) { - rs.push(a[a_ix]); - a_ix += 1; - } else { rs.push(b[b_ix]); b_ix += 1; } - } - rs.push_all(vec::slice(a, a_ix, a_len)); - rs.push_all(vec::slice(b, b_ix, b_len)); - rs - } -} - -fn part<T>(arr: &mut [T], left: uint, - right: uint, pivot: uint, compare_func: Le<T>) -> uint { - vec::swap(arr, pivot, right); - let mut storage_index: uint = left; - let mut i: uint = left; - while i < right { - if compare_func(&arr[i], &arr[right]) { - vec::swap(arr, i, storage_index); - storage_index += 1; - } - i += 1; - } - vec::swap(arr, storage_index, right); - return storage_index; -} - -fn qsort<T>(arr: &mut [T], left: uint, - right: uint, compare_func: Le<T>) { - if right > left { - let pivot = (left + right) / 2u; - let new_pivot = part::<T>(arr, left, right, pivot, compare_func); - if new_pivot != 0u { - // Need to do this check before recursing due to overflow - qsort::<T>(arr, left, new_pivot - 1u, compare_func); - } - qsort::<T>(arr, new_pivot + 1u, right, compare_func); - } -} - -/** - * Quicksort. Sorts a mut vector in place. - * - * Has worst case O(n^2) performance, average case O(n log n). - * This is an unstable sort. - */ -pub fn quick_sort<T>(arr: &mut [T], compare_func: Le<T>) { - if len::<T>(arr) == 0u { return; } - qsort::<T>(arr, 0u, len::<T>(arr) - 1u, compare_func); -} - -fn qsort3<T:Copy + Ord + Eq>(arr: &mut [T], left: int, right: int) { - if right <= left { return; } - let v: T = arr[right]; - let mut i: int = left - 1; - let mut j: int = right; - let mut p: int = i; - let mut q: int = j; - loop { - i += 1; - while arr[i] < v { i += 1; } - j -= 1; - while v < arr[j] { - if j == left { break; } - j -= 1; - } - if i >= j { break; } - vec::swap(arr, i as uint, j as uint); - if arr[i] == v { - p += 1; - vec::swap(arr, p as uint, i as uint); - } - if v == arr[j] { - q -= 1; - vec::swap(arr, j as uint, q as uint); - } - } - vec::swap(arr, i as uint, right as uint); - j = i - 1; - i += 1; - let mut k: int = left; - while k < p { - vec::swap(arr, k as uint, j as uint); - k += 1; - j -= 1; - if k == len::<T>(arr) as int { break; } - } - k = right - 1; - while k > q { - vec::swap(arr, i as uint, k as uint); - k -= 1; - i += 1; - if k == 0 { break; } - } - qsort3::<T>(arr, left, j); - qsort3::<T>(arr, i, right); -} - -/** - * Fancy quicksort. Sorts a mut vector in place. - * - * Based on algorithm presented by ~[Sedgewick and Bentley] - * (http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf). - * According to these slides this is the algorithm of choice for - * 'randomly ordered keys, abstract compare' & 'small number of key values'. - * - * This is an unstable sort. - */ -pub fn quick_sort3<T:Copy + Ord + Eq>(arr: &mut [T]) { - if arr.len() <= 1 { return; } - let len = arr.len(); // FIXME(#5074) nested calls - qsort3(arr, 0, (len - 1) as int); -} - -pub trait Sort { - fn qsort(self); -} - -impl<'self, T:Copy + Ord + Eq> Sort for &'self mut [T] { - fn qsort(self) { quick_sort3(self); } -} - -static MIN_MERGE: uint = 64; -static MIN_GALLOP: uint = 7; -static INITIAL_TMP_STORAGE: uint = 128; - -pub fn tim_sort<T:Copy + Ord>(array: &mut [T]) { - let size = array.len(); - if size < 2 { - return; - } - - if size < MIN_MERGE { - let init_run_len = count_run_ascending(array); - binarysort(array, init_run_len); - return; - } - - let mut ms = MergeState(); - let min_run = min_run_length(size); - - let mut idx = 0; - let mut remaining = size; - loop { - let run_len: uint = { - // This scope contains the slice `arr` here: - let arr = vec::mut_slice(array, idx, size); - let mut run_len: uint = count_run_ascending(arr); - - if run_len < min_run { - let force = if remaining <= min_run {remaining} else {min_run}; - let slice = vec::mut_slice(arr, 0, force); - binarysort(slice, run_len); - run_len = force; - } - - run_len - }; - - ms.push_run(idx, run_len); - ms.merge_collapse(array); - - idx += run_len; - remaining -= run_len; - if remaining == 0 { break; } - } - - ms.merge_force_collapse(array); -} - -fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) { - let size = array.len(); - let mut start = start; - assert!(start <= size); - - if start == 0 { start += 1; } - - while start < size { - let pivot = array[start]; - let mut left = 0; - let mut right = start; - assert!(left <= right); - - while left < right { - let mid = (left + right) >> 1; - if pivot < array[mid] { - right = mid; - } else { - left = mid+1; - } - } - assert_eq!(left, right); - let n = start-left; - - shift_vec(array, left+1, left, n); - array[left] = pivot; - start += 1; - } -} - -// Reverse the order of elements in a slice, in place -fn reverse_slice<T>(v: &mut [T], start: uint, end:uint) { - let mut i = start; - while i < end / 2 { - vec::swap(v, i, end - i - 1); - i += 1; - } -} - -fn min_run_length(n: uint) -> uint { - let mut n = n; - let mut r = 0; // becomes 1 if any 1 bits are shifted off - - while n >= MIN_MERGE { - r |= n & 1; - n >>= 1; - } - return n + r; -} - -fn count_run_ascending<T:Copy + Ord>(array: &mut [T]) -> uint { - let size = array.len(); - assert!(size > 0); - if size == 1 { return 1; } - - let mut run = 2; - if array[1] < array[0] { - while run < size && array[run] < array[run-1] { - run += 1; - } - reverse_slice(array, 0, run); - } else { - while run < size && array[run] >= array[run-1] { - run += 1; - } - } - - return run; -} - -fn gallop_left<T:Copy + Ord>(key: &T, - array: &[T], - hint: uint) - -> uint { - let size = array.len(); - assert!(size != 0 && hint < size); - - let mut last_ofs = 0; - let mut ofs = 1; - - if *key > array[hint] { - // Gallop right until array[hint+last_ofs] < key <= array[hint+ofs] - let max_ofs = size - hint; - while ofs < max_ofs && *key > array[hint+ofs] { - last_ofs = ofs; - ofs = (ofs << 1) + 1; - if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard - } - if ofs > max_ofs { ofs = max_ofs; } - - last_ofs += hint; - ofs += hint; - } else { - let max_ofs = hint + 1; - while ofs < max_ofs && *key <= array[hint-ofs] { - last_ofs = ofs; - ofs = (ofs << 1) + 1; - if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard - } - - if ofs > max_ofs { ofs = max_ofs; } - - let tmp = last_ofs; - last_ofs = hint - ofs; - ofs = hint - tmp; - } - assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size); - - last_ofs += 1; - while last_ofs < ofs { - let m = last_ofs + ((ofs - last_ofs) >> 1); - if *key > array[m] { - last_ofs = m+1; - } else { - ofs = m; - } - } - assert_eq!(last_ofs, ofs); - return ofs; -} - -fn gallop_right<T:Copy + Ord>(key: &T, - array: &[T], - hint: uint) - -> uint { - let size = array.len(); - assert!(size != 0 && hint < size); - - let mut last_ofs = 0; - let mut ofs = 1; - - if *key >= array[hint] { - // Gallop right until array[hint+last_ofs] <= key < array[hint+ofs] - let max_ofs = size - hint; - while ofs < max_ofs && *key >= array[hint+ofs] { - last_ofs = ofs; - ofs = (ofs << 1) + 1; - if ofs < last_ofs { ofs = max_ofs; } - } - if ofs > max_ofs { ofs = max_ofs; } - - last_ofs += hint; - ofs += hint; - } else { - // Gallop left until array[hint-ofs] <= key < array[hint-last_ofs] - let max_ofs = hint + 1; - while ofs < max_ofs && *key < array[hint-ofs] { - last_ofs = ofs; - ofs = (ofs << 1) + 1; - if ofs < last_ofs { ofs = max_ofs; } - } - if ofs > max_ofs { ofs = max_ofs; } - - let tmp = last_ofs; - last_ofs = hint - ofs; - ofs = hint - tmp; - } - - assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size); - - last_ofs += 1; - while last_ofs < ofs { - let m = last_ofs + ((ofs - last_ofs) >> 1); - - if *key >= array[m] { - last_ofs = m + 1; - } else { - ofs = m; - } - } - assert_eq!(last_ofs, ofs); - return ofs; -} - -struct RunState { - base: uint, - len: uint, -} - -struct MergeState<T> { - min_gallop: uint, - runs: ~[RunState], -} - -// Fixme (#3853) Move into MergeState -fn MergeState<T>() -> MergeState<T> { - MergeState { - min_gallop: MIN_GALLOP, - runs: ~[], - } -} - -impl<T:Copy + Ord> MergeState<T> { - fn push_run(&mut self, run_base: uint, run_len: uint) { - let tmp = RunState{base: run_base, len: run_len}; - self.runs.push(tmp); - } - - fn merge_at(&mut self, n: uint, array: &mut [T]) { - let size = self.runs.len(); - assert!(size >= 2); - assert!(n == size-2 || n == size-3); - - let mut b1 = self.runs[n].base; - let mut l1 = self.runs[n].len; - let b2 = self.runs[n+1].base; - let l2 = self.runs[n+1].len; - - assert!(l1 > 0 && l2 > 0); - assert_eq!(b1 + l1, b2); - - self.runs[n].len = l1 + l2; - if n == size-3 { - self.runs[n+1].base = self.runs[n+2].base; - self.runs[n+1].len = self.runs[n+2].len; - } - - let k = { // constrain lifetime of slice below - let slice = vec::slice(array, b1, b1+l1); - gallop_right(&array[b2], slice, 0) - }; - b1 += k; - l1 -= k; - if l1 != 0 { - let l2 = { // constrain lifetime of slice below - let slice = vec::slice(array, b2, b2+l2); - gallop_left(&array[b1+l1-1],slice,l2-1) - }; - if l2 > 0 { - if l1 <= l2 { - self.merge_lo(array, b1, l1, b2, l2); - } else { - self.merge_hi(array, b1, l1, b2, l2); - } - } - } - self.runs.pop(); - } - - fn merge_lo(&mut self, array: &mut [T], base1: uint, len1: uint, - base2: uint, len2: uint) { - assert!(len1 != 0 && len2 != 0 && base1+len1 == base2); - - let mut tmp = ~[]; - for uint::range(base1, base1+len1) |i| { - tmp.push(array[i]); - } - - let mut c1 = 0; - let mut c2 = base2; - let mut dest = base1; - let mut len1 = len1; - let mut len2 = len2; - - vec::swap(array, dest, c2); - dest += 1; c2 += 1; len2 -= 1; - - if len2 == 0 { - copy_vec(array, dest, tmp.slice(0, len1)); - return; - } - if len1 == 1 { - shift_vec(array, dest, c2, len2); - swap(&mut tmp[c1], &mut array[dest+len2]); - return; - } - - let mut min_gallop = self.min_gallop; - loop { - let mut count1 = 0; - let mut count2 = 0; - let mut break_outer = false; - - loop { - assert!(len1 > 1 && len2 != 0); - if array[c2] < tmp[c1] { - vec::swap(array, dest, c2); - dest += 1; c2 += 1; len2 -= 1; - count2 += 1; count1 = 0; - if len2 == 0 { - break_outer = true; - } - } else { - swap(&mut array[dest], &mut tmp[c1]); - dest += 1; c1 += 1; len1 -= 1; - count1 += 1; count2 = 0; - if len1 == 1 { - break_outer = true; - } - } - if break_outer || ((count1 | count2) >= min_gallop) { - break; - } - } - if break_outer { break; } - - // Start to gallop - loop { - assert!(len1 > 1 && len2 != 0); - - count1 = { - let tmp_view = vec::slice(tmp, c1, c1+len1); - gallop_right(&array[c2], tmp_view, 0) - }; - if count1 != 0 { - copy_vec(array, dest, tmp.slice(c1, c1+count1)); - dest += count1; c1 += count1; len1 -= count1; - if len1 <= 1 { break_outer = true; break; } - } - vec::swap(array, dest, c2); - dest += 1; c2 += 1; len2 -= 1; - if len2 == 0 { break_outer = true; break; } - - count2 = { - let tmp_view = vec::slice(array, c2, c2+len2); - gallop_left(&tmp[c1], tmp_view, 0) - }; - if count2 != 0 { - shift_vec(array, dest, c2, count2); - dest += count2; c2 += count2; len2 -= count2; - if len2 == 0 { break_outer = true; break; } - } - swap(&mut array[dest], &mut tmp[c1]); - dest += 1; c1 += 1; len1 -= 1; - if len1 == 1 { break_outer = true; break; } - min_gallop -= 1; - if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) { - break; - } - } - if break_outer { break; } - if min_gallop < 0 { min_gallop = 0; } - min_gallop += 2; // Penalize for leaving gallop - } - self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop }; - - if len1 == 1 { - assert!(len2 > 0); - shift_vec(array, dest, c2, len2); - swap(&mut array[dest+len2], &mut tmp[c1]); - } else if len1 == 0 { - fail!("Comparison violates its contract!"); - } else { - assert_eq!(len2, 0); - assert!(len1 > 1); - copy_vec(array, dest, tmp.slice(c1, c1+len1)); - } - } - - fn merge_hi(&mut self, array: &mut [T], base1: uint, len1: uint, - base2: uint, len2: uint) { - assert!(len1 != 1 && len2 != 0 && base1 + len1 == base2); - - let mut tmp = ~[]; - for uint::range(base2, base2+len2) |i| { - tmp.push(array[i]); - } - - let mut c1 = base1 + len1 - 1; - let mut c2 = len2 - 1; - let mut dest = base2 + len2 - 1; - let mut len1 = len1; - let mut len2 = len2; - - vec::swap(array, dest, c1); - dest -= 1; c1 -= 1; len1 -= 1; - - if len1 == 0 { - copy_vec(array, dest-(len2-1), tmp.slice(0, len2)); - return; - } - if len2 == 1 { - dest -= len1; - c1 -= len1; - shift_vec(array, dest+1, c1+1, len1); - swap(&mut array[dest], &mut tmp[c2]); - return; - } - - let mut min_gallop = self.min_gallop; - loop { - let mut count1 = 0; - let mut count2 = 0; - let mut break_outer = false; - - loop { - assert!(len1 != 0 && len2 > 1); - if tmp[c2] < array[c1] { - vec::swap(array, dest, c1); - dest -= 1; c1 -= 1; len1 -= 1; - count1 += 1; count2 = 0; - if len1 == 0 { - break_outer = true; - } - } else { - swap(&mut array[dest], &mut tmp[c2]); - dest -= 1; c2 -= 1; len2 -= 1; - count2 += 1; count1 = 0; - if len2 == 1 { - break_outer = true; - } - } - if break_outer || ((count1 | count2) >= min_gallop) { - break; - } - } - if break_outer { break; } - - // Start to gallop - loop { - assert!(len2 > 1 && len1 != 0); - - { // constrain scope of tmp_view: - let tmp_view = vec::mut_slice (array, base1, base1+len1); - count1 = len1 - gallop_right( - &tmp[c2], tmp_view, len1-1); - } - - if count1 != 0 { - dest -= count1; c1 -= count1; len1 -= count1; - shift_vec(array, dest+1, c1+1, count1); - if len1 == 0 { break_outer = true; break; } - } - - swap(&mut array[dest], &mut tmp[c2]); - dest -= 1; c2 -= 1; len2 -= 1; - if len2 == 1 { break_outer = true; break; } - - let count2; - { // constrain scope of tmp_view - let tmp_view = vec::mut_slice(tmp, 0, len2); - count2 = len2 - gallop_left(&array[c1], - tmp_view, - len2-1); - } - - if count2 != 0 { - dest -= count2; c2 -= count2; len2 -= count2; - copy_vec(array, dest+1, tmp.slice(c2+1, c2+1+count2)); - if len2 <= 1 { break_outer = true; break; } - } - vec::swap(array, dest, c1); - dest -= 1; c1 -= 1; len1 -= 1; - if len1 == 0 { break_outer = true; break; } - min_gallop -= 1; - if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) { - break; - } - } - - if break_outer { break; } - if min_gallop < 0 { min_gallop = 0; } - min_gallop += 2; // Penalize for leaving gallop - } - self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop }; - - if len2 == 1 { - assert!(len1 > 0); - dest -= len1; - c1 -= len1; - shift_vec(array, dest+1, c1+1, len1); - swap(&mut array[dest], &mut tmp[c2]); - } else if len2 == 0 { - fail!("Comparison violates its contract!"); - } else { - assert_eq!(len1, 0); - assert!(len2 != 0); - copy_vec(array, dest-(len2-1), tmp.slice(0, len2)); - } - } - - fn merge_collapse(&mut self, array: &mut [T]) { - while self.runs.len() > 1 { - let mut n = self.runs.len()-2; - if n > 0 && - self.runs[n-1].len <= self.runs[n].len + self.runs[n+1].len - { - if self.runs[n-1].len < self.runs[n+1].len { n -= 1; } - } else if self.runs[n].len <= self.runs[n+1].len { - /* keep going */ - } else { - break; - } - self.merge_at(n, array); - } - } - - fn merge_force_collapse(&mut self, array: &mut [T]) { - while self.runs.len() > 1 { - let mut n = self.runs.len()-2; - if n > 0 { - if self.runs[n-1].len < self.runs[n+1].len { - n -= 1; - } - } - self.merge_at(n, array); - } - } -} - -#[inline(always)] -fn copy_vec<T:Copy>(dest: &mut [T], - s1: uint, - from: &[T]) { - assert!(s1+from.len() <= dest.len()); - - for from.eachi |i, v| { - dest[s1+i] = *v; - } -} - -#[inline(always)] -fn shift_vec<T:Copy>(dest: &mut [T], - s1: uint, - s2: uint, - len: uint) { - assert!(s1+len <= dest.len()); - - let tmp = dest.slice(s2, s2+len).to_vec(); - copy_vec(dest, s1, tmp); -} - -#[cfg(test)] -mod test_qsort3 { - use sort::*; - - use core::vec; - - fn check_sort(v1: &mut [int], v2: &mut [int]) { - let len = vec::len::<int>(v1); - quick_sort3::<int>(v1); - let mut i = 0; - while i < len { - assert_eq!(v2[i], v1[i]); - i += 1; - } - } - - #[test] - fn test() { - { - let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; - let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; - check_sort(v1, v2); - } - { - let mut v1 = ~[1, 1, 1]; - let mut v2 = ~[1, 1, 1]; - check_sort(v1, v2); - } - { - let mut v1: ~[int] = ~[]; - let mut v2: ~[int] = ~[]; - check_sort(v1, v2); - } - { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } - { - let mut v1 = ~[9, 3, 3, 3, 9]; - let mut v2 = ~[3, 3, 3, 9, 9]; - check_sort(v1, v2); - } - } -} - -#[cfg(test)] -mod test_qsort { - use sort::*; - - use core::int; - use core::vec; - - fn check_sort(v1: &mut [int], v2: &mut [int]) { - let len = vec::len::<int>(v1); - fn leual(a: &int, b: &int) -> bool { *a <= *b } - quick_sort::<int>(v1, leual); - let mut i = 0u; - while i < len { - // debug!(v2[i]); - assert_eq!(v2[i], v1[i]); - i += 1; - } - } - - #[test] - fn test() { - { - let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; - let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; - check_sort(v1, v2); - } - { - let mut v1 = ~[1, 1, 1]; - let mut v2 = ~[1, 1, 1]; - check_sort(v1, v2); - } - { - let mut v1: ~[int] = ~[]; - let mut v2: ~[int] = ~[]; - check_sort(v1, v2); - } - { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } - { - let mut v1 = ~[9, 3, 3, 3, 9]; - let mut v2 = ~[3, 3, 3, 9, 9]; - check_sort(v1, v2); - } - } - - // Regression test for #750 - #[test] - fn test_simple() { - let mut names = ~[2, 1, 3]; - - let expected = ~[1, 2, 3]; - - do quick_sort(names) |x, y| { int::le(*x, *y) }; - - let immut_names = names; - - let pairs = vec::zip_slice(expected, immut_names); - for pairs.each |p| { - let (a, b) = *p; - debug!("%d %d", a, b); - assert_eq!(a, b); - } - } -} - -#[cfg(test)] -mod tests { - - use sort::*; - - use core::vec; - - fn check_sort(v1: &[int], v2: &[int]) { - let len = vec::len::<int>(v1); - pub fn le(a: &int, b: &int) -> bool { *a <= *b } - let f = le; - let v3 = merge_sort::<int>(v1, f); - let mut i = 0u; - while i < len { - debug!(v3[i]); - assert_eq!(v3[i], v2[i]); - i += 1; - } - } - - #[test] - fn test() { - { - let v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; - let v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; - check_sort(v1, v2); - } - { let v1 = ~[1, 1, 1]; let v2 = ~[1, 1, 1]; check_sort(v1, v2); } - { let v1:~[int] = ~[]; let v2:~[int] = ~[]; check_sort(v1, v2); } - { let v1 = ~[9]; let v2 = ~[9]; check_sort(v1, v2); } - { - let v1 = ~[9, 3, 3, 3, 9]; - let v2 = ~[3, 3, 3, 9, 9]; - check_sort(v1, v2); - } - } - - #[test] - fn test_merge_sort_mutable() { - pub fn le(a: &int, b: &int) -> bool { *a <= *b } - let mut v1 = ~[3, 2, 1]; - let v2 = merge_sort(v1, le); - assert_eq!(v2, ~[1, 2, 3]); - } - - #[test] - fn test_merge_sort_stability() { - // tjc: funny that we have to use parens - fn ile(x: &(&'static str), y: &(&'static str)) -> bool - { - // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use - // to_ascii_consume and to_str_consume to not do a unnecessary copy. - // (Actually, could just remove the to_str_* call, but needs an deriving(Ord) on - // Ascii) - let x = x.to_ascii().to_lower().to_str_ascii(); - let y = y.to_ascii().to_lower().to_str_ascii(); - x <= y - } - - let names1 = ~["joe bob", "Joe Bob", "Jack Brown", "JOE Bob", - "Sally Mae", "JOE BOB", "Alex Andy"]; - let names2 = ~["Alex Andy", "Jack Brown", "joe bob", "Joe Bob", - "JOE Bob", "JOE BOB", "Sally Mae"]; - let names3 = merge_sort(names1, ile); - assert_eq!(names3, names2); - } -} - -#[cfg(test)] -mod test_tim_sort { - use sort::tim_sort; - use core::rand::RngUtil; - - struct CVal { - val: float, - } - - impl Ord for CVal { - fn lt(&self, other: &CVal) -> bool { - let mut rng = rand::rng(); - if rng.gen::<float>() > 0.995 { - fail!("It's happening!!!"); - } - (*self).val < other.val - } - fn le(&self, other: &CVal) -> bool { (*self).val <= other.val } - fn gt(&self, other: &CVal) -> bool { (*self).val > other.val } - fn ge(&self, other: &CVal) -> bool { (*self).val >= other.val } - } - - fn check_sort(v1: &mut [int], v2: &mut [int]) { - let len = vec::len::<int>(v1); - tim_sort::<int>(v1); - let mut i = 0u; - while i < len { - // debug!(v2[i]); - assert_eq!(v2[i], v1[i]); - i += 1u; - } - } - - #[test] - fn test() { - { - let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; - let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; - check_sort(v1, v2); - } - { - let mut v1 = ~[1, 1, 1]; - let mut v2 = ~[1, 1, 1]; - check_sort(v1, v2); - } - { - let mut v1: ~[int] = ~[]; - let mut v2: ~[int] = ~[]; - check_sort(v1, v2); - } - { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } - { - let mut v1 = ~[9, 3, 3, 3, 9]; - let mut v2 = ~[3, 3, 3, 9, 9]; - check_sort(v1, v2); - } - } - - #[test] - #[should_fail] - #[cfg(unix)] - fn crash_test() { - let mut rng = rand::rng(); - let mut arr = do vec::from_fn(1000) |_i| { - CVal { val: rng.gen() } - }; - - tim_sort(arr); - fail!("Guarantee the fail"); - } - - struct DVal { val: uint } - - impl Ord for DVal { - fn lt(&self, _x: &DVal) -> bool { true } - fn le(&self, _x: &DVal) -> bool { true } - fn gt(&self, _x: &DVal) -> bool { true } - fn ge(&self, _x: &DVal) -> bool { true } - } - - #[test] - fn test_bad_Ord_impl() { - let mut rng = rand::rng(); - let mut arr = do vec::from_fn(500) |_i| { - DVal { val: rng.gen() } - }; - - tim_sort(arr); - } -} - -#[cfg(test)] -mod big_tests { - use sort::*; - use core::rand::RngUtil; - - #[test] - fn test_unique() { - let low = 5; - let high = 10; - tabulate_unique(low, high); - } - - #[test] - fn test_managed() { - let low = 5; - let high = 10; - tabulate_managed(low, high); - } - - fn multiplyVec<T:Copy>(arr: &[T], num: uint) -> ~[T] { - let size = arr.len(); - let res = do vec::from_fn(num) |i| { - arr[i % size] - }; - res - } - - fn makeRange(n: uint) -> ~[uint] { - let one = do vec::from_fn(n) |i| { i }; - let mut two = copy one; - vec::reverse(two); - vec::append(two, one) - } - - fn tabulate_unique(lo: uint, hi: uint) { - fn isSorted<T:Ord>(arr: &[T]) { - for uint::range(0, arr.len()-1) |i| { - if arr[i] > arr[i+1] { - fail!("Array not sorted"); - } - } - } - - let mut rng = rand::rng(); - - for uint::range(lo, hi) |i| { - let n = 1 << i; - let mut arr: ~[float] = do vec::from_fn(n) |_i| { - rng.gen() - }; - - tim_sort(arr); // *sort - isSorted(arr); - - vec::reverse(arr); - tim_sort(arr); // \sort - isSorted(arr); - - tim_sort(arr); // /sort - isSorted(arr); - - for 3.times { - let i1 = rng.gen_uint_range(0, n); - let i2 = rng.gen_uint_range(0, n); - vec::swap(arr, i1, i2); - } - tim_sort(arr); // 3sort - isSorted(arr); - - if n >= 10 { - let size = arr.len(); - let mut idx = 1; - while idx <= 10 { - arr[size-idx] = rng.gen(); - idx += 1; - } - } - tim_sort(arr); // +sort - isSorted(arr); - - for (n/100).times { - let idx = rng.gen_uint_range(0, n); - arr[idx] = rng.gen(); - } - tim_sort(arr); - isSorted(arr); - - let mut arr = if n > 4 { - let part = vec::slice(arr, 0, 4); - multiplyVec(part, n) - } else { arr }; - tim_sort(arr); // ~sort - isSorted(arr); - - let mut arr = vec::from_elem(n, -0.5); - tim_sort(arr); // =sort - isSorted(arr); - - let half = n / 2; - let mut arr = makeRange(half).map(|i| *i as float); - tim_sort(arr); // !sort - isSorted(arr); - } - } - - fn tabulate_managed(lo: uint, hi: uint) { - fn isSorted<T:Ord>(arr: &[@T]) { - for uint::range(0, arr.len()-1) |i| { - if arr[i] > arr[i+1] { - fail!("Array not sorted"); - } - } - } - - let mut rng = rand::rng(); - - for uint::range(lo, hi) |i| { - let n = 1 << i; - let arr: ~[@float] = do vec::from_fn(n) |_i| { - @rng.gen() - }; - let mut arr = arr; - - tim_sort(arr); // *sort - isSorted(arr); - - vec::reverse(arr); - tim_sort(arr); // \sort - isSorted(arr); - - tim_sort(arr); // /sort - isSorted(arr); - - for 3.times { - let i1 = rng.gen_uint_range(0, n); - let i2 = rng.gen_uint_range(0, n); - vec::swap(arr, i1, i2); - } - tim_sort(arr); // 3sort - isSorted(arr); - - if n >= 10 { - let size = arr.len(); - let mut idx = 1; - while idx <= 10 { - arr[size-idx] = @rng.gen(); - idx += 1; - } - } - tim_sort(arr); // +sort - isSorted(arr); - - for (n/100).times { - let idx = rng.gen_uint_range(0, n); - arr[idx] = @rng.gen(); - } - tim_sort(arr); - isSorted(arr); - - let mut arr = if n > 4 { - let part = vec::slice(arr, 0, 4); - multiplyVec(part, n) - } else { arr }; - tim_sort(arr); // ~sort - isSorted(arr); - - let mut arr = vec::from_elem(n, @(-0.5)); - tim_sort(arr); // =sort - isSorted(arr); - - let half = n / 2; - let mut arr = makeRange(half).map(|i| @(*i as float)); - tim_sort(arr); // !sort - isSorted(arr); - } - } - - struct LVal<'self> { - val: uint, - key: &'self fn(@uint), - } - - #[unsafe_destructor] - impl<'self> Drop for LVal<'self> { - fn finalize(&self) { - let x = unsafe { local_data::local_data_get(self.key) }; - match x { - Some(@y) => { - unsafe { - local_data::local_data_set(self.key, @(y+1)); - } - } - _ => fail!("Expected key to work"), - } - } - } - - impl<'self> Ord for LVal<'self> { - fn lt<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val < other.val - } - fn le<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val <= other.val - } - fn gt<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val > other.val - } - fn ge<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val >= other.val - } - } -} diff --git a/src/libstd/stackwalk.rs b/src/libstd/stackwalk.rs new file mode 100644 index 00000000000..784656718d0 --- /dev/null +++ b/src/libstd/stackwalk.rs @@ -0,0 +1,81 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; // FIXME #3538 + +use cast::transmute; +use unstable::intrinsics; + +pub type Word = uint; + +pub struct Frame { + fp: *Word +} + +pub fn Frame(fp: *Word) -> Frame { + Frame { + fp: fp + } +} + +pub fn walk_stack(visit: &fn(Frame) -> bool) -> bool { + + debug!("beginning stack walk"); + + do frame_address |frame_pointer| { + let mut frame_address: *Word = unsafe { + transmute(frame_pointer) + }; + loop { + let fr = Frame(frame_address); + + debug!("frame: %x", unsafe { transmute(fr.fp) }); + visit(fr); + + unsafe { + let next_fp: **Word = transmute(frame_address); + frame_address = *next_fp; + if *frame_address == 0u { + debug!("encountered task_start_wrapper. ending walk"); + // This is the task_start_wrapper_frame. There is + // no stack beneath it and it is a foreign frame. + break; + } + } + } + } + return true; +} + +#[test] +fn test_simple() { + for walk_stack |_frame| { + } +} + +#[test] +fn test_simple_deep() { + fn run(i: int) { + if i == 0 { return } + + for walk_stack |_frame| { + // Would be nice to test something here... + } + run(i - 1); + } + + run(10); +} + +fn frame_address(f: &fn(x: *u8)) { + unsafe { + intrinsics::frame_address(f) + } +} diff --git a/src/libstd/stats.rs b/src/libstd/stats.rs deleted file mode 100644 index 25323b4e1db..00000000000 --- a/src/libstd/stats.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 core::vec; -use core::f64; -use core::cmp; -use core::num; -use sort; - -// NB: this can probably be rewritten in terms of num::Num -// to be less f64-specific. - -pub trait Stats { - fn sum(self) -> f64; - fn min(self) -> f64; - fn max(self) -> f64; - fn mean(self) -> f64; - fn median(self) -> f64; - fn var(self) -> f64; - fn std_dev(self) -> f64; - fn std_dev_pct(self) -> f64; - fn median_abs_dev(self) -> f64; - fn median_abs_dev_pct(self) -> f64; -} - -impl<'self> Stats for &'self [f64] { - fn sum(self) -> f64 { - vec::foldl(0.0, self, |p,q| p + *q) - } - - fn min(self) -> f64 { - assert!(self.len() != 0); - vec::foldl(self[0], self, |p,q| cmp::min(p, *q)) - } - - fn max(self) -> f64 { - assert!(self.len() != 0); - vec::foldl(self[0], self, |p,q| cmp::max(p, *q)) - } - - fn mean(self) -> f64 { - assert!(self.len() != 0); - self.sum() / (self.len() as f64) - } - - fn median(self) -> f64 { - assert!(self.len() != 0); - let mut tmp = vec::to_owned(self); - sort::tim_sort(tmp); - if tmp.len() & 1 == 0 { - let m = tmp.len() / 2; - (tmp[m] + tmp[m-1]) / 2.0 - } else { - tmp[tmp.len() / 2] - } - } - - fn var(self) -> f64 { - if self.len() == 0 { - 0.0 - } else { - let mean = self.mean(); - let mut v = 0.0; - for self.each |s| { - let x = *s - mean; - v += x*x; - } - v/(self.len() as f64) - } - } - - fn std_dev(self) -> f64 { - f64::sqrt(self.var()) - } - - fn std_dev_pct(self) -> f64 { - (self.std_dev() / self.mean()) * 100.0 - } - - fn median_abs_dev(self) -> f64 { - let med = self.median(); - let abs_devs = self.map(|v| num::abs(med - *v)); - abs_devs.median() - } - - fn median_abs_dev_pct(self) -> f64 { - (self.median_abs_dev() / self.median()) * 100.0 - } -} diff --git a/src/libstd/std.rc b/src/libstd/std.rc deleted file mode 100644 index 72f06f0befa..00000000000 --- a/src/libstd/std.rc +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - -The Rust standard library. - -The Rust standand library provides a number of useful features that are -not required in or otherwise suitable for the core library. - -*/ - -#[link(name = "std", - vers = "0.7-pre", - uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297", - url = "https://github.com/mozilla/rust/tree/master/src/libstd")]; - -#[comment = "The Rust standard library"]; -#[license = "MIT/ASL2"]; -#[crate_type = "lib"]; - -#[deny(non_camel_case_types)]; - -pub mod uv_ll; - -// General io and system-services modules - -pub mod net; -pub mod net_ip; -pub mod net_tcp; -pub mod net_url; - -// libuv modules -pub mod uv; -pub mod uv_iotask; -pub mod uv_global_loop; - - -// Utility modules - -pub mod c_vec; -pub mod timer; -pub mod io_util; -pub mod rc; - -// Concurrency - -pub mod sync; -pub mod arc; -pub mod comm; -pub mod future; -pub mod task_pool; -pub mod flatpipes; - -// Collections - -pub mod bitv; -pub mod deque; -pub mod fun_treemap; -pub mod list; -pub mod priority_queue; -pub mod rope; -pub mod smallintmap; - -pub mod sort; - -pub mod dlist; -pub mod treemap; - -// And ... other stuff - -pub mod ebml; -pub mod dbg; -pub mod getopts; -pub mod json; -pub mod sha1; -pub mod md4; -pub mod tempfile; -pub mod term; -pub mod time; -pub mod arena; -pub mod par; -pub mod base64; -pub mod rl; -pub mod workcache; -#[path="num/bigint.rs"] -pub mod bigint; -#[path="num/rational.rs"] -pub mod rational; -#[path="num/complex.rs"] -pub mod complex; -pub mod stats; -pub mod semver; -pub mod fileinput; -pub mod flate; - -#[cfg(unicode)] -mod unicode; - - -// Compiler support modules - -pub mod test; -pub mod serialize; - -// A curious inner-module that's not exported that contains the binding -// 'std' so that macro-expanded references to std::serialize and such -// can be resolved within libcore. -#[doc(hidden)] // FIXME #3538 -pub mod std { - pub use serialize; - pub use test; -} diff --git a/src/libstd/str.rs b/src/libstd/str.rs new file mode 100644 index 00000000000..53169554ec5 --- /dev/null +++ b/src/libstd/str.rs @@ -0,0 +1,4036 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + * String manipulation + * + * Strings are a packed UTF-8 representation of text, stored as null + * terminated buffers of u8 bytes. Strings should be indexed in bytes, + * for efficiency, but UTF-8 unsafe operations should be avoided. For + * some heavy-duty uses, try std::rope. + */ + +use at_vec; +use cast::transmute; +use cast; +use char; +use clone::Clone; +use cmp::{TotalOrd, Ordering, Less, Equal, Greater}; +use container::Container; +use iter::Times; +use iterator::Iterator; +use libc; +use option::{None, Option, Some}; +use old_iter::{BaseIter, EqIter}; +use ptr; +use ptr::Ptr; +use str; +use to_str::ToStr; +use uint; +use vec; +use vec::{OwnedVector, OwnedCopyableVector}; + +#[cfg(not(test))] use cmp::{Eq, Ord, Equiv, TotalEq}; + +/* +Section: Creating a string +*/ + +/** + * Convert a vector of bytes to a new UTF-8 string + * + * # Failure + * + * Fails if invalid UTF-8 + */ +pub fn from_bytes(vv: &const [u8]) -> ~str { + assert!(is_utf8(vv)); + return unsafe { raw::from_bytes(vv) }; +} + +/** + * Convert a vector of bytes to a UTF-8 string. + * The vector needs to be one byte longer than the string, and end with a 0 byte. + * + * Compared to `from_bytes()`, this fn doesn't need to allocate a new owned str. + * + * # Failure + * + * Fails if invalid UTF-8 + * Fails if not null terminated + */ +pub fn from_bytes_with_null<'a>(vv: &'a [u8]) -> &'a str { + assert_eq!(vv[vv.len() - 1], 0); + assert!(is_utf8(vv)); + return unsafe { raw::from_bytes_with_null(vv) }; +} + +pub fn from_bytes_slice<'a>(vector: &'a [u8]) -> &'a str { + unsafe { + assert!(is_utf8(vector)); + let (ptr, len): (*u8, uint) = ::cast::transmute(vector); + let string: &'a str = ::cast::transmute((ptr, len + 1)); + string + } +} + +/// Copy a slice into a new unique str +#[inline(always)] +pub fn to_owned(s: &str) -> ~str { + unsafe { raw::slice_bytes_owned(s, 0, len(s)) } +} + +impl ToStr for ~str { + #[inline(always)] + fn to_str(&self) -> ~str { to_owned(*self) } +} +impl<'self> ToStr for &'self str { + #[inline(always)] + fn to_str(&self) -> ~str { to_owned(*self) } +} +impl ToStr for @str { + #[inline(always)] + fn to_str(&self) -> ~str { to_owned(*self) } +} + +/** + * Convert a byte to a UTF-8 string + * + * # Failure + * + * Fails if invalid UTF-8 + */ +pub fn from_byte(b: u8) -> ~str { + assert!(b < 128u8); + unsafe { ::cast::transmute(~[b, 0u8]) } +} + +/// Appends a character at the end of a string +pub fn push_char(s: &mut ~str, ch: char) { + unsafe { + let code = ch as uint; + let nb = if code < max_one_b { 1u } + else if code < max_two_b { 2u } + else if code < max_three_b { 3u } + else if code < max_four_b { 4u } + else if code < max_five_b { 5u } + else { 6u }; + let len = len(*s); + let new_len = len + nb; + reserve_at_least(&mut *s, new_len); + let off = len; + do as_buf(*s) |buf, _len| { + let buf: *mut u8 = ::cast::transmute(buf); + match nb { + 1u => { + *ptr::mut_offset(buf, off) = code as u8; + } + 2u => { + *ptr::mut_offset(buf, off) = (code >> 6u & 31u | tag_two_b) as u8; + *ptr::mut_offset(buf, off + 1u) = (code & 63u | tag_cont) as u8; + } + 3u => { + *ptr::mut_offset(buf, off) = (code >> 12u & 15u | tag_three_b) as u8; + *ptr::mut_offset(buf, off + 1u) = (code >> 6u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 2u) = (code & 63u | tag_cont) as u8; + } + 4u => { + *ptr::mut_offset(buf, off) = (code >> 18u & 7u | tag_four_b) as u8; + *ptr::mut_offset(buf, off + 1u) = (code >> 12u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 2u) = (code >> 6u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 3u) = (code & 63u | tag_cont) as u8; + } + 5u => { + *ptr::mut_offset(buf, off) = (code >> 24u & 3u | tag_five_b) as u8; + *ptr::mut_offset(buf, off + 1u) = (code >> 18u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 2u) = (code >> 12u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 3u) = (code >> 6u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 4u) = (code & 63u | tag_cont) as u8; + } + 6u => { + *ptr::mut_offset(buf, off) = (code >> 30u & 1u | tag_six_b) as u8; + *ptr::mut_offset(buf, off + 1u) = (code >> 24u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 2u) = (code >> 18u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 3u) = (code >> 12u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 4u) = (code >> 6u & 63u | tag_cont) as u8; + *ptr::mut_offset(buf, off + 5u) = (code & 63u | tag_cont) as u8; + } + _ => {} + } + } + raw::set_len(s, new_len); + } +} + +/// Convert a char to a string +pub fn from_char(ch: char) -> ~str { + let mut buf = ~""; + push_char(&mut buf, ch); + buf +} + +/// Convert a vector of chars to a string +pub fn from_chars(chs: &[char]) -> ~str { + let mut buf = ~""; + reserve(&mut buf, chs.len()); + for chs.each |ch| { + push_char(&mut buf, *ch); + } + buf +} + +/// Appends a string slice to the back of a string, without overallocating +#[inline(always)] +pub fn push_str_no_overallocate(lhs: &mut ~str, rhs: &str) { + unsafe { + let llen = lhs.len(); + let rlen = rhs.len(); + reserve(&mut *lhs, llen + rlen); + do as_buf(*lhs) |lbuf, _llen| { + do as_buf(rhs) |rbuf, _rlen| { + let dst = ptr::offset(lbuf, llen); + let dst = ::cast::transmute_mut_unsafe(dst); + ptr::copy_memory(dst, rbuf, rlen); + } + } + raw::set_len(lhs, llen + rlen); + } +} + +/// Appends a string slice to the back of a string +#[inline(always)] +pub fn push_str(lhs: &mut ~str, rhs: &str) { + unsafe { + let llen = lhs.len(); + let rlen = rhs.len(); + reserve_at_least(&mut *lhs, llen + rlen); + do as_buf(*lhs) |lbuf, _llen| { + do as_buf(rhs) |rbuf, _rlen| { + let dst = ptr::offset(lbuf, llen); + let dst = ::cast::transmute_mut_unsafe(dst); + ptr::copy_memory(dst, rbuf, rlen); + } + } + raw::set_len(lhs, llen + rlen); + } +} + +/// Concatenate two strings together +#[inline(always)] +pub fn append(lhs: ~str, rhs: &str) -> ~str { + let mut v = lhs; + push_str_no_overallocate(&mut v, rhs); + v +} + +/// Concatenate a vector of strings +pub fn concat(v: &[~str]) -> ~str { + if v.is_empty() { return ~""; } + + let mut len = 0; + for v.each |ss| { + len += ss.len(); + } + let mut s = ~""; + + reserve(&mut s, len); + + unsafe { + do as_buf(s) |buf, _len| { + let mut buf = ::cast::transmute_mut_unsafe(buf); + for v.each |ss| { + do as_buf(*ss) |ssbuf, sslen| { + let sslen = sslen - 1; + ptr::copy_memory(buf, ssbuf, sslen); + buf = buf.offset(sslen); + } + } + } + raw::set_len(&mut s, len); + } + s +} + +/// Concatenate a vector of strings, placing a given separator between each +pub fn connect(v: &[~str], sep: &str) -> ~str { + if v.is_empty() { return ~""; } + + // concat is faster + if sep.is_empty() { return concat(v); } + + // this is wrong without the guarantee that v is non-empty + let mut len = sep.len() * (v.len() - 1); + for v.each |ss| { + len += ss.len(); + } + let mut s = ~"", first = true; + + reserve(&mut s, len); + + unsafe { + do as_buf(s) |buf, _len| { + do as_buf(sep) |sepbuf, seplen| { + let seplen = seplen - 1; + let mut buf = ::cast::transmute_mut_unsafe(buf); + for v.each |ss| { + do as_buf(*ss) |ssbuf, sslen| { + let sslen = sslen - 1; + if first { + first = false; + } else { + ptr::copy_memory(buf, sepbuf, seplen); + buf = buf.offset(seplen); + } + ptr::copy_memory(buf, ssbuf, sslen); + buf = buf.offset(sslen); + } + } + } + } + raw::set_len(&mut s, len); + } + s +} + +/// Concatenate a vector of strings, placing a given separator between each +pub fn connect_slices(v: &[&str], sep: &str) -> ~str { + if v.is_empty() { return ~""; } + + // this is wrong without the guarantee that v is non-empty + let mut len = sep.len() * (v.len() - 1); + for v.each |ss| { + len += ss.len(); + } + let mut s = ~"", first = true; + + reserve(&mut s, len); + + unsafe { + do as_buf(s) |buf, _len| { + do as_buf(sep) |sepbuf, seplen| { + let seplen = seplen - 1; + let mut buf = ::cast::transmute_mut_unsafe(buf); + for v.each |ss| { + do as_buf(*ss) |ssbuf, sslen| { + let sslen = sslen - 1; + if first { + first = false; + } else if seplen > 0 { + ptr::copy_memory(buf, sepbuf, seplen); + buf = buf.offset(seplen); + } + ptr::copy_memory(buf, ssbuf, sslen); + buf = buf.offset(sslen); + } + } + } + } + raw::set_len(&mut s, len); + } + s +} + +/// Given a string, make a new string with repeated copies of it +pub fn repeat(ss: &str, nn: uint) -> ~str { + do as_buf(ss) |buf, len| { + let mut ret = ~""; + // ignore the NULL terminator + let len = len - 1; + reserve(&mut ret, nn * len); + + unsafe { + do as_buf(ret) |rbuf, _len| { + let mut rbuf = ::cast::transmute_mut_unsafe(rbuf); + + for nn.times { + ptr::copy_memory(rbuf, buf, len); + rbuf = rbuf.offset(len); + } + } + raw::set_len(&mut ret, nn * len); + } + ret + } +} + +/* +Section: Adding to and removing from a string +*/ + +/** + * Remove the final character from a string and return it + * + * # Failure + * + * If the string does not contain any characters + */ +pub fn pop_char(s: &mut ~str) -> char { + let end = len(*s); + assert!(end > 0u); + let CharRange {ch, next} = char_range_at_reverse(*s, end); + unsafe { raw::set_len(s, next); } + return ch; +} + +/** + * Remove the first character from a string and return it + * + * # Failure + * + * If the string does not contain any characters + */ +pub fn shift_char(s: &mut ~str) -> char { + let CharRange {ch, next} = char_range_at(*s, 0u); + *s = unsafe { raw::slice_bytes_owned(*s, next, len(*s)) }; + return ch; +} + +/** + * Removes the first character from a string slice and returns it. This does + * not allocate a new string; instead, it mutates a slice to point one + * character beyond the character that was shifted. + * + * # Failure + * + * If the string does not contain any characters + */ +#[inline] +pub fn slice_shift_char<'a>(s: &'a str) -> (char, &'a str) { + let CharRange {ch, next} = char_range_at(s, 0u); + let next_s = unsafe { raw::slice_bytes(s, next, len(s)) }; + return (ch, next_s); +} + +/// Prepend a char to a string +pub fn unshift_char(s: &mut ~str, ch: char) { + // This could be more efficient. + let mut new_str = ~""; + new_str.push_char(ch); + new_str.push_str(*s); + *s = new_str; +} + +/** + * Returns a string with leading `chars_to_trim` removed. + * + * # Arguments + * + * * s - A string + * * chars_to_trim - A vector of chars + * + */ +pub fn trim_left_chars<'a>(s: &'a str, chars_to_trim: &[char]) -> &'a str { + if chars_to_trim.is_empty() { return s; } + + match find(s, |c| !chars_to_trim.contains(&c)) { + None => "", + Some(first) => unsafe { raw::slice_bytes(s, first, s.len()) } + } +} + +/** + * Returns a string with trailing `chars_to_trim` removed. + * + * # Arguments + * + * * s - A string + * * chars_to_trim - A vector of chars + * + */ +pub fn trim_right_chars<'a>(s: &'a str, chars_to_trim: &[char]) -> &'a str { + if chars_to_trim.is_empty() { return s; } + + match rfind(s, |c| !chars_to_trim.contains(&c)) { + None => "", + Some(last) => { + let next = char_range_at(s, last).next; + unsafe { raw::slice_bytes(s, 0u, next) } + } + } +} + +/** + * Returns a string with leading and trailing `chars_to_trim` removed. + * + * # Arguments + * + * * s - A string + * * chars_to_trim - A vector of chars + * + */ +pub fn trim_chars<'a>(s: &'a str, chars_to_trim: &[char]) -> &'a str { + trim_left_chars(trim_right_chars(s, chars_to_trim), chars_to_trim) +} + +/// Returns a string with leading whitespace removed +pub fn trim_left<'a>(s: &'a str) -> &'a str { + match find(s, |c| !char::is_whitespace(c)) { + None => "", + Some(first) => unsafe { raw::slice_bytes(s, first, len(s)) } + } +} + +/// Returns a string with trailing whitespace removed +pub fn trim_right<'a>(s: &'a str) -> &'a str { + match rfind(s, |c| !char::is_whitespace(c)) { + None => "", + Some(last) => { + let next = char_range_at(s, last).next; + unsafe { raw::slice_bytes(s, 0u, next) } + } + } +} + +/// Returns a string with leading and trailing whitespace removed +pub fn trim<'a>(s: &'a str) -> &'a str { trim_left(trim_right(s)) } + +/* +Section: Transforming strings +*/ + +/** + * Converts a string to a unique vector of bytes + * + * The result vector is not null-terminated. + */ +pub fn to_bytes(s: &str) -> ~[u8] { + unsafe { + let mut v: ~[u8] = ::cast::transmute(to_owned(s)); + vec::raw::set_len(&mut v, len(s)); + v + } +} + +/// Work with the string as a byte slice, not including trailing null. +#[inline(always)] +pub fn byte_slice<T>(s: &str, f: &fn(v: &[u8]) -> T) -> T { + do as_buf(s) |p,n| { + unsafe { vec::raw::buf_as_slice(p, n-1u, f) } + } +} + +/// Work with the string as a byte slice, not including trailing null, without +/// a callback. +#[inline(always)] +pub fn byte_slice_no_callback<'a>(s: &'a str) -> &'a [u8] { + unsafe { + cast::transmute(s) + } +} + +/// Convert a string to a unique vector of characters +pub fn to_chars(s: &str) -> ~[char] { + let mut buf = ~[]; + for each_char(s) |c| { + buf.push(c); + } + buf +} + +/** + * Take a substring of another. + * + * Returns a slice pointing at `n` characters starting from byte offset + * `begin`. + */ +pub fn substr<'a>(s: &'a str, begin: uint, n: uint) -> &'a str { + slice(s, begin, begin + count_bytes(s, begin, n)) +} + +/** + * Returns a slice of the given string from the byte range [`begin`..`end`) + * + * Fails when `begin` and `end` do not point to valid characters or beyond + * the last character of the string + */ +pub fn slice<'a>(s: &'a str, begin: uint, end: uint) -> &'a str { + assert!(is_char_boundary(s, begin)); + assert!(is_char_boundary(s, end)); + unsafe { raw::slice_bytes(s, begin, end) } +} + +/// Splits a string into substrings at each occurrence of a given character +pub fn each_split_char<'a>(s: &'a str, sep: char, + it: &fn(&'a str) -> bool) -> bool { + each_split_char_inner(s, sep, len(s), true, true, it) +} + +/// Like `each_split_char`, but a trailing empty string is omitted +pub fn each_split_char_no_trailing<'a>(s: &'a str, + sep: char, + it: &fn(&'a str) -> bool) -> bool { + each_split_char_inner(s, sep, len(s), true, false, it) +} + +/** + * Splits a string into substrings at each occurrence of a given + * character up to 'count' times. + * + * The character must be a valid UTF-8/ASCII character + */ +pub fn each_splitn_char<'a>(s: &'a str, + sep: char, + count: uint, + it: &fn(&'a str) -> bool) -> bool { + each_split_char_inner(s, sep, count, true, true, it) +} + +/// Like `each_split_char`, but omits empty strings +pub fn each_split_char_nonempty<'a>(s: &'a str, + sep: char, + it: &fn(&'a str) -> bool) -> bool { + each_split_char_inner(s, sep, len(s), false, false, it) +} + +fn each_split_char_inner<'a>(s: &'a str, + sep: char, + count: uint, + allow_empty: bool, + allow_trailing_empty: bool, + it: &fn(&'a str) -> bool) -> bool { + if sep < 128u as char { + let b = sep as u8, l = len(s); + let mut done = 0u; + let mut i = 0u, start = 0u; + while i < l && done < count { + if s[i] == b { + if allow_empty || start < i { + if !it( unsafe{ raw::slice_bytes(s, start, i) } ) { + return false; + } + } + start = i + 1u; + done += 1u; + } + i += 1u; + } + // only slice a non-empty trailing substring + if allow_trailing_empty || start < l { + if !it( unsafe{ raw::slice_bytes(s, start, l) } ) { return false; } + } + return true; + } + return each_split_inner(s, |cur| cur == sep, count, + allow_empty, allow_trailing_empty, it) +} + +/// Splits a string into substrings using a character function +pub fn each_split<'a>(s: &'a str, + sepfn: &fn(char) -> bool, + it: &fn(&'a str) -> bool) -> bool { + each_split_inner(s, sepfn, len(s), true, true, it) +} + +/// Like `each_split`, but a trailing empty string is omitted +pub fn each_split_no_trailing<'a>(s: &'a str, + sepfn: &fn(char) -> bool, + it: &fn(&'a str) -> bool) -> bool { + each_split_inner(s, sepfn, len(s), true, false, it) +} + +/** + * Splits a string into substrings using a character function, cutting at + * most `count` times. + */ +pub fn each_splitn<'a>(s: &'a str, + sepfn: &fn(char) -> bool, + count: uint, + it: &fn(&'a str) -> bool) -> bool { + each_split_inner(s, sepfn, count, true, true, it) +} + +/// Like `each_split`, but omits empty strings +pub fn each_split_nonempty<'a>(s: &'a str, + sepfn: &fn(char) -> bool, + it: &fn(&'a str) -> bool) -> bool { + each_split_inner(s, sepfn, len(s), false, false, it) +} + +fn each_split_inner<'a>(s: &'a str, + sepfn: &fn(cc: char) -> bool, + count: uint, + allow_empty: bool, + allow_trailing_empty: bool, + it: &fn(&'a str) -> bool) -> bool { + let l = len(s); + let mut i = 0u, start = 0u, done = 0u; + while i < l && done < count { + let CharRange {ch, next} = char_range_at(s, i); + if sepfn(ch) { + if allow_empty || start < i { + if !it( unsafe{ raw::slice_bytes(s, start, i) } ) { + return false; + } + } + start = next; + done += 1u; + } + i = next; + } + if allow_trailing_empty || start < l { + if !it( unsafe{ raw::slice_bytes(s, start, l) } ) { return false; } + } + return true; +} + +// See Issue #1932 for why this is a naive search +fn iter_matches<'a,'b>(s: &'a str, sep: &'b str, + f: &fn(uint, uint) -> bool) -> bool { + let sep_len = len(sep), l = len(s); + assert!(sep_len > 0u); + let mut i = 0u, match_start = 0u, match_i = 0u; + + while i < l { + if s[i] == sep[match_i] { + if match_i == 0u { match_start = i; } + match_i += 1u; + // Found a match + if match_i == sep_len { + if !f(match_start, i + 1u) { return false; } + match_i = 0u; + } + i += 1u; + } else { + // Failed match, backtrack + if match_i > 0u { + match_i = 0u; + i = match_start + 1u; + } else { + i += 1u; + } + } + } + return true; +} + +fn iter_between_matches<'a,'b>(s: &'a str, + sep: &'b str, + f: &fn(uint, uint) -> bool) -> bool { + let mut last_end = 0u; + for iter_matches(s, sep) |from, to| { + if !f(last_end, from) { return false; } + last_end = to; + } + return f(last_end, len(s)); +} + +/** + * Splits a string into a vector of the substrings separated by a given string + * + * # Example + * + * ~~~ + * let mut v = ~[]; + * for each_split_str(".XXX.YYY.", ".") |subs| { v.push(subs); } + * assert!(v == ["", "XXX", "YYY", ""]); + * ~~~ + */ +pub fn each_split_str<'a,'b>(s: &'a str, + sep: &'b str, + it: &fn(&'a str) -> bool) -> bool { + for iter_between_matches(s, sep) |from, to| { + if !it( unsafe { raw::slice_bytes(s, from, to) } ) { return false; } + } + return true; +} + +pub fn each_split_str_nonempty<'a,'b>(s: &'a str, + sep: &'b str, + it: &fn(&'a str) -> bool) -> bool { + for iter_between_matches(s, sep) |from, to| { + if to > from { + if !it( unsafe { raw::slice_bytes(s, from, to) } ) { return false; } + } + } + return true; +} + +/// Levenshtein Distance between two strings +pub fn levdistance(s: &str, t: &str) -> uint { + + let slen = s.len(); + let tlen = t.len(); + + if slen == 0 { return tlen; } + if tlen == 0 { return slen; } + + let mut dcol = vec::from_fn(tlen + 1, |x| x); + + for s.each_chari |i, sc| { + + let mut current = i; + dcol[0] = current + 1; + + for t.each_chari |j, tc| { + + let next = dcol[j + 1]; + + if sc == tc { + dcol[j + 1] = current; + } else { + dcol[j + 1] = ::cmp::min(current, next); + dcol[j + 1] = ::cmp::min(dcol[j + 1], dcol[j]) + 1; + } + + current = next; + } + } + + return dcol[tlen]; +} + +/** + * Splits a string into substrings separated by LF ('\n'). + */ +pub fn each_line<'a>(s: &'a str, it: &fn(&'a str) -> bool) -> bool { + each_split_char_no_trailing(s, '\n', it) +} + +/** + * Splits a string into substrings separated by LF ('\n') + * and/or CR LF ("\r\n") + */ +pub fn each_line_any<'a>(s: &'a str, it: &fn(&'a str) -> bool) -> bool { + for each_line(s) |s| { + let l = s.len(); + if l > 0u && s[l - 1u] == '\r' as u8 { + if !it( unsafe { raw::slice_bytes(s, 0, l - 1) } ) { return false; } + } else { + if !it( s ) { return false; } + } + } + return true; +} + +/// Splits a string into substrings separated by whitespace +pub fn each_word<'a>(s: &'a str, it: &fn(&'a str) -> bool) -> bool { + each_split_nonempty(s, char::is_whitespace, it) +} + +/** Splits a string into substrings with possibly internal whitespace, + * each of them at most `lim` bytes long. The substrings have leading and trailing + * whitespace removed, and are only cut at whitespace boundaries. + * + * #Failure: + * + * Fails during iteration if the string contains a non-whitespace + * sequence longer than the limit. + */ +pub fn _each_split_within<'a>(ss: &'a str, + lim: uint, + it: &fn(&'a str) -> bool) -> bool { + // Just for fun, let's write this as an state machine: + + enum SplitWithinState { + A, // leading whitespace, initial state + B, // words + C, // internal and trailing whitespace + } + enum Whitespace { + Ws, // current char is whitespace + Cr // current char is not whitespace + } + enum LengthLimit { + UnderLim, // current char makes current substring still fit in limit + OverLim // current char makes current substring no longer fit in limit + } + + let mut slice_start = 0; + let mut last_start = 0; + let mut last_end = 0; + let mut state = A; + + let mut cont = true; + let slice: &fn() = || { cont = it(slice(ss, slice_start, last_end)) }; + + let machine: &fn(uint, char) -> bool = |i, c| { + let whitespace = if char::is_whitespace(c) { Ws } else { Cr }; + let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim }; + + state = match (state, whitespace, limit) { + (A, Ws, _) => { A } + (A, Cr, _) => { slice_start = i; last_start = i; B } + + (B, Cr, UnderLim) => { B } + (B, Cr, OverLim) if (i - last_start + 1) > lim + => fail!("word starting with %? longer than limit!", + self::slice(ss, last_start, i + 1)), + (B, Cr, OverLim) => { slice(); slice_start = last_start; B } + (B, Ws, UnderLim) => { last_end = i; C } + (B, Ws, OverLim) => { last_end = i; slice(); A } + + (C, Cr, UnderLim) => { last_start = i; B } + (C, Cr, OverLim) => { slice(); slice_start = i; last_start = i; last_end = i; B } + (C, Ws, OverLim) => { slice(); A } + (C, Ws, UnderLim) => { C } + }; + + cont + }; + + str::each_chari(ss, machine); + + // Let the automaton 'run out' by supplying trailing whitespace + let mut fake_i = ss.len(); + while cont && match state { B | C => true, A => false } { + machine(fake_i, ' '); + fake_i += 1; + } + return cont; +} + +pub fn each_split_within<'a>(ss: &'a str, + lim: uint, + it: &fn(&'a str) -> bool) -> bool { + _each_split_within(ss, lim, it) +} + +/** + * Replace all occurrences of one string with another + * + * # Arguments + * + * * s - The string containing substrings to replace + * * from - The string to replace + * * to - The replacement string + * + * # Return value + * + * The original string with all occurances of `from` replaced with `to` + */ +pub fn replace(s: &str, from: &str, to: &str) -> ~str { + let mut result = ~"", first = true; + for iter_between_matches(s, from) |start, end| { + if first { + first = false; + } else { + push_str(&mut result, to); + } + push_str(&mut result, unsafe{raw::slice_bytes(s, start, end)}); + } + result +} + +/* +Section: Comparing strings +*/ + +/// Bytewise slice equality +#[cfg(not(test))] +#[lang="str_eq"] +#[inline] +pub fn eq_slice(a: &str, b: &str) -> bool { + do as_buf(a) |ap, alen| { + do as_buf(b) |bp, blen| { + if (alen != blen) { false } + else { + unsafe { + libc::memcmp(ap as *libc::c_void, + bp as *libc::c_void, + (alen - 1) as libc::size_t) == 0 + } + } + } + } +} + +#[cfg(test)] +#[inline] +pub fn eq_slice(a: &str, b: &str) -> bool { + do as_buf(a) |ap, alen| { + do as_buf(b) |bp, blen| { + if (alen != blen) { false } + else { + unsafe { + libc::memcmp(ap as *libc::c_void, + bp as *libc::c_void, + (alen - 1) as libc::size_t) == 0 + } + } + } + } +} + +/// Bytewise string equality +#[cfg(not(test))] +#[lang="uniq_str_eq"] +#[inline] +pub fn eq(a: &~str, b: &~str) -> bool { + eq_slice(*a, *b) +} + +#[cfg(test)] +#[inline] +pub fn eq(a: &~str, b: &~str) -> bool { + eq_slice(*a, *b) +} + +#[inline] +fn cmp(a: &str, b: &str) -> Ordering { + let low = uint::min(a.len(), b.len()); + + for uint::range(0, low) |idx| { + match a[idx].cmp(&b[idx]) { + Greater => return Greater, + Less => return Less, + Equal => () + } + } + + a.len().cmp(&b.len()) +} + +#[cfg(not(test))] +impl<'self> TotalOrd for &'self str { + #[inline] + fn cmp(&self, other: & &'self str) -> Ordering { cmp(*self, *other) } +} + +#[cfg(not(test))] +impl TotalOrd for ~str { + #[inline] + fn cmp(&self, other: &~str) -> Ordering { cmp(*self, *other) } +} + +#[cfg(not(test))] +impl TotalOrd for @str { + #[inline] + fn cmp(&self, other: &@str) -> Ordering { cmp(*self, *other) } +} + +/// Bytewise slice less than +#[inline] +fn lt(a: &str, b: &str) -> bool { + let (a_len, b_len) = (a.len(), b.len()); + let end = uint::min(a_len, b_len); + + let mut i = 0; + while i < end { + let (c_a, c_b) = (a[i], b[i]); + if c_a < c_b { return true; } + if c_a > c_b { return false; } + i += 1; + } + + return a_len < b_len; +} + +/// Bytewise less than or equal +#[inline] +pub fn le(a: &str, b: &str) -> bool { + !lt(b, a) +} + +/// Bytewise greater than or equal +#[inline] +fn ge(a: &str, b: &str) -> bool { + !lt(a, b) +} + +/// Bytewise greater than +#[inline] +fn gt(a: &str, b: &str) -> bool { + !le(a, b) +} + +#[cfg(not(test))] +impl<'self> Eq for &'self str { + #[inline(always)] + fn eq(&self, other: & &'self str) -> bool { + eq_slice((*self), (*other)) + } + #[inline(always)] + fn ne(&self, other: & &'self str) -> bool { !(*self).eq(other) } +} + +#[cfg(not(test))] +impl Eq for ~str { + #[inline(always)] + fn eq(&self, other: &~str) -> bool { + eq_slice((*self), (*other)) + } + #[inline(always)] + fn ne(&self, other: &~str) -> bool { !(*self).eq(other) } +} + +#[cfg(not(test))] +impl Eq for @str { + #[inline(always)] + fn eq(&self, other: &@str) -> bool { + eq_slice((*self), (*other)) + } + #[inline(always)] + fn ne(&self, other: &@str) -> bool { !(*self).eq(other) } +} + +#[cfg(not(test))] +impl<'self> TotalEq for &'self str { + #[inline(always)] + fn equals(&self, other: & &'self str) -> bool { + eq_slice((*self), (*other)) + } +} + +#[cfg(not(test))] +impl TotalEq for ~str { + #[inline(always)] + fn equals(&self, other: &~str) -> bool { + eq_slice((*self), (*other)) + } +} + +#[cfg(not(test))] +impl TotalEq for @str { + #[inline(always)] + fn equals(&self, other: &@str) -> bool { + eq_slice((*self), (*other)) + } +} + +#[cfg(not(test))] +impl Ord for ~str { + #[inline(always)] + fn lt(&self, other: &~str) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: &~str) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: &~str) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: &~str) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +impl<'self> Ord for &'self str { + #[inline(always)] + fn lt(&self, other: & &'self str) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: & &'self str) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: & &'self str) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: & &'self str) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +impl Ord for @str { + #[inline(always)] + fn lt(&self, other: &@str) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: &@str) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: &@str) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: &@str) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +impl<'self> Equiv<~str> for &'self str { + #[inline(always)] + fn equiv(&self, other: &~str) -> bool { eq_slice(*self, *other) } +} + +/* +Section: Iterating through strings +*/ + +/** + * Return true if a predicate matches all characters or if the string + * contains no characters + */ +pub fn all(s: &str, it: &fn(char) -> bool) -> bool { + all_between(s, 0u, len(s), it) +} + +/** + * Return true if a predicate matches any character (and false if it + * matches none or there are no characters) + */ +pub fn any(ss: &str, pred: &fn(char) -> bool) -> bool { + !all(ss, |cc| !pred(cc)) +} + +/// Apply a function to each character +pub fn map(ss: &str, ff: &fn(char) -> char) -> ~str { + let mut result = ~""; + reserve(&mut result, len(ss)); + for ss.each_char |cc| { + str::push_char(&mut result, ff(cc)); + } + result +} + +/// Iterate over the bytes in a string +#[inline(always)] +pub fn each(s: &str, it: &fn(u8) -> bool) -> bool { + eachi(s, |_i, b| it(b)) +} + +/// Iterate over the bytes in a string, with indices +#[inline(always)] +pub fn eachi(s: &str, it: &fn(uint, u8) -> bool) -> bool { + let mut pos = 0; + let len = s.len(); + + while pos < len { + if !it(pos, s[pos]) { return false; } + pos += 1; + } + return true; +} + +/// Iterate over the bytes in a string in reverse +#[inline(always)] +pub fn each_reverse(s: &str, it: &fn(u8) -> bool) -> bool { + eachi_reverse(s, |_i, b| it(b) ) +} + +/// Iterate over the bytes in a string in reverse, with indices +#[inline(always)] +pub fn eachi_reverse(s: &str, it: &fn(uint, u8) -> bool) -> bool { + let mut pos = s.len(); + while pos > 0 { + pos -= 1; + if !it(pos, s[pos]) { return false; } + } + return true; +} + +/// Iterate over each char of a string, without allocating +#[inline(always)] +pub fn each_char(s: &str, it: &fn(char) -> bool) -> bool { + let mut i = 0; + let len = len(s); + while i < len { + let CharRange {ch, next} = char_range_at(s, i); + if !it(ch) { return false; } + i = next; + } + return true; +} + +/// Iterates over the chars in a string, with indices +#[inline(always)] +pub fn each_chari(s: &str, it: &fn(uint, char) -> bool) -> bool { + let mut pos = 0; + let mut ch_pos = 0u; + let len = s.len(); + while pos < len { + let CharRange {ch, next} = char_range_at(s, pos); + pos = next; + if !it(ch_pos, ch) { return false; } + ch_pos += 1u; + } + return true; +} + +/// Iterates over the chars in a string in reverse +#[inline(always)] +pub fn each_char_reverse(s: &str, it: &fn(char) -> bool) -> bool { + each_chari_reverse(s, |_, c| it(c)) +} + +// Iterates over the chars in a string in reverse, with indices +#[inline(always)] +pub fn each_chari_reverse(s: &str, it: &fn(uint, char) -> bool) -> bool { + let mut pos = s.len(); + let mut ch_pos = s.char_len(); + while pos > 0 { + let CharRange {ch, next} = char_range_at_reverse(s, pos); + pos = next; + ch_pos -= 1; + + if !it(ch_pos, ch) { return false; } + } + return true; +} + +/* +Section: Searching +*/ + +/** + * Returns the byte index of the first matching character + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + */ +pub fn find_char(s: &str, c: char) -> Option<uint> { + find_char_between(s, c, 0u, len(s)) +} + +/** + * Returns the byte index of the first matching character beginning + * from a given byte offset + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * * `start` - The byte index to begin searching at, inclusive + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `len(s)`. `start` must be the + * index of a character boundary, as defined by `is_char_boundary`. + */ +pub fn find_char_from(s: &str, c: char, start: uint) -> Option<uint> { + find_char_between(s, c, start, len(s)) +} + +/** + * Returns the byte index of the first matching character within a given range + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * * `start` - The byte index to begin searching at, inclusive + * * `end` - The byte index to end searching at, exclusive + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `end` and `end` must be less than + * or equal to `len(s)`. `start` must be the index of a character boundary, + * as defined by `is_char_boundary`. + */ +pub fn find_char_between(s: &str, c: char, start: uint, end: uint) + -> Option<uint> { + if c < 128u as char { + assert!(start <= end); + assert!(end <= len(s)); + let mut i = start; + let b = c as u8; + while i < end { + if s[i] == b { return Some(i); } + i += 1u; + } + return None; + } else { + find_between(s, start, end, |x| x == c) + } +} + +/** + * Returns the byte index of the last matching character + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + */ +pub fn rfind_char(s: &str, c: char) -> Option<uint> { + rfind_char_between(s, c, len(s), 0u) +} + +/** + * Returns the byte index of the last matching character beginning + * from a given byte offset + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * * `start` - The byte index to begin searching at, exclusive + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `len(s)`. `start` must be + * the index of a character boundary, as defined by `is_char_boundary`. + */ +pub fn rfind_char_from(s: &str, c: char, start: uint) -> Option<uint> { + rfind_char_between(s, c, start, 0u) +} + +/** + * Returns the byte index of the last matching character within a given range + * + * # Arguments + * + * * `s` - The string to search + * * `c` - The character to search for + * * `start` - The byte index to begin searching at, exclusive + * * `end` - The byte index to end searching at, inclusive + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + * + * # Failure + * + * `end` must be less than or equal to `start` and `start` must be less than + * or equal to `len(s)`. `start` must be the index of a character boundary, + * as defined by `is_char_boundary`. + */ +pub fn rfind_char_between(s: &str, c: char, start: uint, end: uint) -> Option<uint> { + if c < 128u as char { + assert!(start >= end); + assert!(start <= len(s)); + let mut i = start; + let b = c as u8; + while i > end { + i -= 1u; + if s[i] == b { return Some(i); } + } + return None; + } else { + rfind_between(s, start, end, |x| x == c) + } +} + +/** + * Returns the byte index of the first character that satisfies + * the given predicate + * + * # Arguments + * + * * `s` - The string to search + * * `f` - The predicate to satisfy + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + */ +pub fn find(s: &str, f: &fn(char) -> bool) -> Option<uint> { + find_between(s, 0u, len(s), f) +} + +/** + * Returns the byte index of the first character that satisfies + * the given predicate, beginning from a given byte offset + * + * # Arguments + * + * * `s` - The string to search + * * `start` - The byte index to begin searching at, inclusive + * * `f` - The predicate to satisfy + * + * # Return value + * + * An `option` containing the byte index of the first matching charactor + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `len(s)`. `start` must be the + * index of a character boundary, as defined by `is_char_boundary`. + */ +pub fn find_from(s: &str, start: uint, f: &fn(char) + -> bool) -> Option<uint> { + find_between(s, start, len(s), f) +} + +/** + * Returns the byte index of the first character that satisfies + * the given predicate, within a given range + * + * # Arguments + * + * * `s` - The string to search + * * `start` - The byte index to begin searching at, inclusive + * * `end` - The byte index to end searching at, exclusive + * * `f` - The predicate to satisfy + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `end` and `end` must be less than + * or equal to `len(s)`. `start` must be the index of a character + * boundary, as defined by `is_char_boundary`. + */ +pub fn find_between(s: &str, start: uint, end: uint, f: &fn(char) -> bool) -> Option<uint> { + assert!(start <= end); + assert!(end <= len(s)); + assert!(is_char_boundary(s, start)); + let mut i = start; + while i < end { + let CharRange {ch, next} = char_range_at(s, i); + if f(ch) { return Some(i); } + i = next; + } + return None; +} + +/** + * Returns the byte index of the last character that satisfies + * the given predicate + * + * # Arguments + * + * * `s` - The string to search + * * `f` - The predicate to satisfy + * + * # Return value + * + * An option containing the byte index of the last matching character + * or `none` if there is no match + */ +pub fn rfind(s: &str, f: &fn(char) -> bool) -> Option<uint> { + rfind_between(s, len(s), 0u, f) +} + +/** + * Returns the byte index of the last character that satisfies + * the given predicate, beginning from a given byte offset + * + * # Arguments + * + * * `s` - The string to search + * * `start` - The byte index to begin searching at, exclusive + * * `f` - The predicate to satisfy + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `len(s)', `start` must be the + * index of a character boundary, as defined by `is_char_boundary` + */ +pub fn rfind_from(s: &str, start: uint, f: &fn(char) -> bool) -> Option<uint> { + rfind_between(s, start, 0u, f) +} + +/** + * Returns the byte index of the last character that satisfies + * the given predicate, within a given range + * + * # Arguments + * + * * `s` - The string to search + * * `start` - The byte index to begin searching at, exclusive + * * `end` - The byte index to end searching at, inclusive + * * `f` - The predicate to satisfy + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + * + * # Failure + * + * `end` must be less than or equal to `start` and `start` must be less + * than or equal to `len(s)`. `start` must be the index of a character + * boundary, as defined by `is_char_boundary` + */ +pub fn rfind_between(s: &str, start: uint, end: uint, f: &fn(char) -> bool) -> Option<uint> { + assert!(start >= end); + assert!(start <= len(s)); + assert!(is_char_boundary(s, start)); + let mut i = start; + while i > end { + let CharRange {ch, next: prev} = char_range_at_reverse(s, i); + if f(ch) { return Some(prev); } + i = prev; + } + return None; +} + +// Utility used by various searching functions +fn match_at<'a,'b>(haystack: &'a str, needle: &'b str, at: uint) -> bool { + let mut i = at; + for each(needle) |c| { if haystack[i] != c { return false; } i += 1u; } + return true; +} + +/** + * Returns the byte index of the first matching substring + * + * # Arguments + * + * * `haystack` - The string to search + * * `needle` - The string to search for + * + * # Return value + * + * An `option` containing the byte index of the first matching substring + * or `none` if there is no match + */ +pub fn find_str<'a,'b>(haystack: &'a str, needle: &'b str) -> Option<uint> { + find_str_between(haystack, needle, 0u, len(haystack)) +} + +/** + * Returns the byte index of the first matching substring beginning + * from a given byte offset + * + * # Arguments + * + * * `haystack` - The string to search + * * `needle` - The string to search for + * * `start` - The byte index to begin searching at, inclusive + * + * # Return value + * + * An `option` containing the byte index of the last matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `len(s)` + */ +pub fn find_str_from<'a,'b>(haystack: &'a str, + needle: &'b str, + start: uint) + -> Option<uint> { + find_str_between(haystack, needle, start, len(haystack)) +} + +/** + * Returns the byte index of the first matching substring within a given range + * + * # Arguments + * + * * `haystack` - The string to search + * * `needle` - The string to search for + * * `start` - The byte index to begin searching at, inclusive + * * `end` - The byte index to end searching at, exclusive + * + * # Return value + * + * An `option` containing the byte index of the first matching character + * or `none` if there is no match + * + * # Failure + * + * `start` must be less than or equal to `end` and `end` must be less than + * or equal to `len(s)`. + */ +pub fn find_str_between<'a,'b>(haystack: &'a str, + needle: &'b str, + start: uint, + end:uint) + -> Option<uint> { + // See Issue #1932 for why this is a naive search + assert!(end <= len(haystack)); + let needle_len = len(needle); + if needle_len == 0u { return Some(start); } + if needle_len > end { return None; } + + let mut i = start; + let e = end - needle_len; + while i <= e { + if match_at(haystack, needle, i) { return Some(i); } + i += 1u; + } + return None; +} + +/** + * Returns true if one string contains another + * + * # Arguments + * + * * haystack - The string to look in + * * needle - The string to look for + */ +pub fn contains<'a,'b>(haystack: &'a str, needle: &'b str) -> bool { + find_str(haystack, needle).is_some() +} + +/** + * Returns true if a string contains a char. + * + * # Arguments + * + * * haystack - The string to look in + * * needle - The char to look for + */ +pub fn contains_char(haystack: &str, needle: char) -> bool { + find_char(haystack, needle).is_some() +} + +/** + * Returns true if one string starts with another + * + * # Arguments + * + * * haystack - The string to look in + * * needle - The string to look for + */ +pub fn starts_with<'a,'b>(haystack: &'a str, needle: &'b str) -> bool { + let haystack_len = len(haystack), needle_len = len(needle); + if needle_len == 0u { true } + else if needle_len > haystack_len { false } + else { match_at(haystack, needle, 0u) } +} + +/** + * Returns true if one string ends with another + * + * # Arguments + * + * * haystack - The string to look in + * * needle - The string to look for + */ +pub fn ends_with<'a,'b>(haystack: &'a str, needle: &'b str) -> bool { + let haystack_len = len(haystack), needle_len = len(needle); + if needle_len == 0u { true } + else if needle_len > haystack_len { false } + else { match_at(haystack, needle, haystack_len - needle_len) } +} + +/* +Section: String properties +*/ + +/// Returns true if the string has length 0 +#[inline(always)] +pub fn is_empty(s: &str) -> bool { len(s) == 0u } + +/** + * Returns true if the string contains only whitespace + * + * Whitespace characters are determined by `char::is_whitespace` + */ +pub fn is_whitespace(s: &str) -> bool { + return all(s, char::is_whitespace); +} + +/** + * Returns true if the string contains only alphanumerics + * + * Alphanumeric characters are determined by `char::is_alphanumeric` + */ +fn is_alphanumeric(s: &str) -> bool { + return all(s, char::is_alphanumeric); +} + +/// Returns the string length/size in bytes not counting the null terminator +#[inline(always)] +pub fn len(s: &str) -> uint { + do as_buf(s) |_p, n| { n - 1u } +} + +/// Returns the number of characters that a string holds +#[inline(always)] +pub fn char_len(s: &str) -> uint { count_chars(s, 0u, len(s)) } + +/* +Section: Misc +*/ + +/// Determines if a vector of bytes contains valid UTF-8 +pub fn is_utf8(v: &const [u8]) -> bool { + let mut i = 0u; + let total = vec::len::<u8>(v); + while i < total { + let mut chsize = utf8_char_width(v[i]); + if chsize == 0u { return false; } + if i + chsize > total { return false; } + i += 1u; + while chsize > 1u { + if v[i] & 192u8 != tag_cont_u8 { return false; } + i += 1u; + chsize -= 1u; + } + } + return true; +} + +/// Determines if a vector of `u16` contains valid UTF-16 +pub fn is_utf16(v: &[u16]) -> bool { + let len = v.len(); + let mut i = 0u; + while (i < len) { + let u = v[i]; + + if u <= 0xD7FF_u16 || u >= 0xE000_u16 { + i += 1u; + + } else { + if i+1u < len { return false; } + let u2 = v[i+1u]; + if u < 0xD7FF_u16 || u > 0xDBFF_u16 { return false; } + if u2 < 0xDC00_u16 || u2 > 0xDFFF_u16 { return false; } + i += 2u; + } + } + return true; +} + +/// Converts to a vector of `u16` encoded as UTF-16 +pub fn to_utf16(s: &str) -> ~[u16] { + let mut u = ~[]; + for s.each_char |ch| { + // Arithmetic with u32 literals is easier on the eyes than chars. + let mut ch = ch as u32; + + if (ch & 0xFFFF_u32) == ch { + // The BMP falls through (assuming non-surrogate, as it + // should) + assert!(ch <= 0xD7FF_u32 || ch >= 0xE000_u32); + u.push(ch as u16) + } else { + // Supplementary planes break into surrogates. + assert!(ch >= 0x1_0000_u32 && ch <= 0x10_FFFF_u32); + ch -= 0x1_0000_u32; + let w1 = 0xD800_u16 | ((ch >> 10) as u16); + let w2 = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); + u.push_all([w1, w2]) + } + } + u +} + +pub fn utf16_chars(v: &[u16], f: &fn(char)) { + let len = v.len(); + let mut i = 0u; + while (i < len && v[i] != 0u16) { + let u = v[i]; + + if u <= 0xD7FF_u16 || u >= 0xE000_u16 { + f(u as char); + i += 1u; + + } else { + let u2 = v[i+1u]; + assert!(u >= 0xD800_u16 && u <= 0xDBFF_u16); + assert!(u2 >= 0xDC00_u16 && u2 <= 0xDFFF_u16); + let mut c = (u - 0xD800_u16) as char; + c = c << 10; + c |= (u2 - 0xDC00_u16) as char; + c |= 0x1_0000_u32 as char; + f(c); + i += 2u; + } + } +} + +pub fn from_utf16(v: &[u16]) -> ~str { + let mut buf = ~""; + reserve(&mut buf, v.len()); + utf16_chars(v, |ch| push_char(&mut buf, ch)); + buf +} + +pub fn with_capacity(capacity: uint) -> ~str { + let mut buf = ~""; + reserve(&mut buf, capacity); + buf +} + +/** + * As char_len but for a slice of a string + * + * # Arguments + * + * * s - A valid string + * * start - The position inside `s` where to start counting in bytes + * * end - The position where to stop counting + * + * # Return value + * + * The number of Unicode characters in `s` between the given indices. + */ +pub fn count_chars(s: &str, start: uint, end: uint) -> uint { + assert!(is_char_boundary(s, start)); + assert!(is_char_boundary(s, end)); + let mut i = start, len = 0u; + while i < end { + let next = char_range_at(s, i).next; + len += 1u; + i = next; + } + return len; +} + +/// Counts the number of bytes taken by the first `n` chars in `s` +/// starting from `start`. +pub fn count_bytes<'b>(s: &'b str, start: uint, n: uint) -> uint { + assert!(is_char_boundary(s, start)); + let mut end = start, cnt = n; + let l = len(s); + while cnt > 0u { + assert!(end < l); + let next = char_range_at(s, end).next; + cnt -= 1u; + end = next; + } + end - start +} + +/// Given a first byte, determine how many bytes are in this UTF-8 character +pub fn utf8_char_width(b: u8) -> uint { + let byte: uint = b as uint; + if byte < 128u { return 1u; } + // Not a valid start byte + if byte < 192u { return 0u; } + if byte < 224u { return 2u; } + if byte < 240u { return 3u; } + if byte < 248u { return 4u; } + if byte < 252u { return 5u; } + return 6u; +} + +/** + * Returns false if the index points into the middle of a multi-byte + * character sequence. + */ +pub fn is_char_boundary(s: &str, index: uint) -> bool { + if index == len(s) { return true; } + let b = s[index]; + return b < 128u8 || b >= 192u8; +} + +/** + * Pluck a character out of a string and return the index of the next + * character. + * + * This function can be used to iterate over the unicode characters of a + * string. + * + * # Example + * + * ~~~ + * let s = "中华Việt Nam"; + * let i = 0u; + * while i < str::len(s) { + * let CharRange {ch, next} = str::char_range_at(s, i); + * std::io::println(fmt!("%u: %c",i,ch)); + * i = next; + * } + * ~~~ + * + * # Example output + * + * ~~~ + * 0: 中 + * 3: 华 + * 6: V + * 7: i + * 8: ệ + * 11: t + * 12: + * 13: N + * 14: a + * 15: m + * ~~~ + * + * # Arguments + * + * * s - The string + * * i - The byte offset of the char to extract + * + * # Return value + * + * A record {ch: char, next: uint} containing the char value and the byte + * index of the next unicode character. + * + * # Failure + * + * If `i` is greater than or equal to the length of the string. + * If `i` is not the index of the beginning of a valid UTF-8 character. + */ +pub fn char_range_at(s: &str, i: uint) -> CharRange { + let b0 = s[i]; + let w = utf8_char_width(b0); + assert!((w != 0u)); + if w == 1u { return CharRange {ch: b0 as char, next: i + 1u}; } + let mut val = 0u; + let end = i + w; + let mut i = i + 1u; + while i < end { + let byte = s[i]; + assert_eq!(byte & 192u8, tag_cont_u8); + val <<= 6u; + val += (byte & 63u8) as uint; + i += 1u; + } + // Clunky way to get the right bits from the first byte. Uses two shifts, + // the first to clip off the marker bits at the left of the byte, and then + // a second (as uint) to get it to the right position. + val += ((b0 << ((w + 1u) as u8)) as uint) << ((w - 1u) * 6u - w - 1u); + return CharRange {ch: val as char, next: i}; +} + +/// Plucks the character starting at the `i`th byte of a string +pub fn char_at(s: &str, i: uint) -> char { + return char_range_at(s, i).ch; +} + +pub struct CharRange { + ch: char, + next: uint +} + +/** + * Given a byte position and a str, return the previous char and its position. + * + * This function can be used to iterate over a unicode string in reverse. + * + * Returns 0 for next index if called on start index 0. + */ +pub fn char_range_at_reverse(ss: &str, start: uint) -> CharRange { + let mut prev = start; + + // while there is a previous byte == 10...... + while prev > 0u && ss[prev - 1u] & 192u8 == tag_cont_u8 { + prev -= 1u; + } + + // now refer to the initial byte of previous char + if prev > 0u { + prev -= 1u; + } else { + prev = 0u; + } + + + let ch = char_at(ss, prev); + return CharRange {ch:ch, next:prev}; +} + +/// Plucks the character ending at the `i`th byte of a string +pub fn char_at_reverse(s: &str, i: uint) -> char { + char_range_at_reverse(s, i).ch +} + +/** + * Loop through a substring, char by char + * + * # Safety note + * + * * This function does not check whether the substring is valid. + * * This function fails if `start` or `end` do not + * represent valid positions inside `s` + * + * # Arguments + * + * * s - A string to traverse. It may be empty. + * * start - The byte offset at which to start in the string. + * * end - The end of the range to traverse + * * it - A block to execute with each consecutive character of `s`. + * Return `true` to continue, `false` to stop. + * + * # Return value + * + * `true` If execution proceeded correctly, `false` if it was interrupted, + * that is if `it` returned `false` at any point. + */ +pub fn all_between(s: &str, start: uint, end: uint, + it: &fn(char) -> bool) -> bool { + assert!(is_char_boundary(s, start)); + let mut i = start; + while i < end { + let CharRange {ch, next} = char_range_at(s, i); + if !it(ch) { return false; } + i = next; + } + return true; +} + +/** + * Loop through a substring, char by char + * + * # Safety note + * + * * This function does not check whether the substring is valid. + * * This function fails if `start` or `end` do not + * represent valid positions inside `s` + * + * # Arguments + * + * * s - A string to traverse. It may be empty. + * * start - The byte offset at which to start in the string. + * * end - The end of the range to traverse + * * it - A block to execute with each consecutive character of `s`. + * Return `true` to continue, `false` to stop. + * + * # Return value + * + * `true` if `it` returns `true` for any character + */ +pub fn any_between(s: &str, start: uint, end: uint, + it: &fn(char) -> bool) -> bool { + !all_between(s, start, end, |c| !it(c)) +} + +// UTF-8 tags and ranges +static tag_cont_u8: u8 = 128u8; +static tag_cont: uint = 128u; +static max_one_b: uint = 128u; +static tag_two_b: uint = 192u; +static max_two_b: uint = 2048u; +static tag_three_b: uint = 224u; +static max_three_b: uint = 65536u; +static tag_four_b: uint = 240u; +static max_four_b: uint = 2097152u; +static tag_five_b: uint = 248u; +static max_five_b: uint = 67108864u; +static tag_six_b: uint = 252u; + +/** + * Work with the byte buffer of a string. + * + * Allows for unsafe manipulation of strings, which is useful for foreign + * interop. + * + * # Example + * + * ~~~ + * let i = str::as_bytes("Hello World") { |bytes| bytes.len() }; + * ~~~ + */ +#[inline] +pub fn as_bytes<T>(s: &const ~str, f: &fn(&~[u8]) -> T) -> T { + unsafe { + let v: *~[u8] = cast::transmute(copy s); + f(&*v) + } +} + +/** + * Work with the byte buffer of a string as a byte slice. + * + * The byte slice does not include the null terminator. + */ +pub fn as_bytes_slice<'a>(s: &'a str) -> &'a [u8] { + unsafe { + let (ptr, len): (*u8, uint) = ::cast::transmute(s); + let outgoing_tuple: (*u8, uint) = (ptr, len - 1); + return ::cast::transmute(outgoing_tuple); + } +} + +/** + * Work with the byte buffer of a string as a null-terminated C string. + * + * Allows for unsafe manipulation of strings, which is useful for foreign + * interop. This is similar to `str::as_buf`, but guarantees null-termination. + * If the given slice is not already null-terminated, this function will + * allocate a temporary, copy the slice, null terminate it, and pass + * that instead. + * + * # Example + * + * ~~~ + * let s = str::as_c_str("PATH", { |path| libc::getenv(path) }); + * ~~~ + */ +#[inline] +pub fn as_c_str<T>(s: &str, f: &fn(*libc::c_char) -> T) -> T { + do as_buf(s) |buf, len| { + // NB: len includes the trailing null. + assert!(len > 0); + if unsafe { *(ptr::offset(buf,len-1)) != 0 } { + as_c_str(to_owned(s), f) + } else { + f(buf as *libc::c_char) + } + } +} + +/** + * Work with the byte buffer and length of a slice. + * + * The given length is one byte longer than the 'official' indexable + * length of the string. This is to permit probing the byte past the + * indexable area for a null byte, as is the case in slices pointing + * to full strings, or suffixes of them. + */ +#[inline(always)] +pub fn as_buf<T>(s: &str, f: &fn(*u8, uint) -> T) -> T { + unsafe { + let v : *(*u8,uint) = transmute(&s); + let (buf,len) = *v; + f(buf, len) + } +} + +/** + * Returns the byte offset of an inner slice relative to an enclosing outer slice + * + * # Example + * + * ~~~ + * let string = "a\nb\nc"; + * let mut lines = ~[]; + * for each_line(string) |line| { lines.push(line) } + * + * assert!(subslice_offset(string, lines[0]) == 0); // &"a" + * assert!(subslice_offset(string, lines[1]) == 2); // &"b" + * assert!(subslice_offset(string, lines[2]) == 4); // &"c" + * ~~~ + */ +#[inline(always)] +pub fn subslice_offset(outer: &str, inner: &str) -> uint { + do as_buf(outer) |a, a_len| { + do as_buf(inner) |b, b_len| { + let a_start: uint, a_end: uint, b_start: uint, b_end: uint; + unsafe { + a_start = cast::transmute(a); a_end = a_len + cast::transmute(a); + b_start = cast::transmute(b); b_end = b_len + cast::transmute(b); + } + assert!(a_start <= b_start); + assert!(b_end <= a_end); + b_start - a_start + } + } +} + +/** + * Reserves capacity for exactly `n` bytes in the given string, not including + * the null terminator. + * + * Assuming single-byte characters, the resulting string will be large + * enough to hold a string of length `n`. To account for the null terminator, + * the underlying buffer will have the size `n` + 1. + * + * If the capacity for `s` is already equal to or greater than the requested + * capacity, then no action is taken. + * + * # Arguments + * + * * s - A string + * * n - The number of bytes to reserve space for + */ +#[inline(always)] +pub fn reserve(s: &mut ~str, n: uint) { + unsafe { + let v: *mut ~[u8] = cast::transmute(s); + vec::reserve(&mut *v, n + 1); + } +} + +/** + * Reserves capacity for at least `n` bytes in the given string, not including + * the null terminator. + * + * Assuming single-byte characters, the resulting string will be large + * enough to hold a string of length `n`. To account for the null terminator, + * the underlying buffer will have the size `n` + 1. + * + * This function will over-allocate in order to amortize the allocation costs + * in scenarios where the caller may need to repeatedly reserve additional + * space. + * + * If the capacity for `s` is already equal to or greater than the requested + * capacity, then no action is taken. + * + * # Arguments + * + * * s - A string + * * n - The number of bytes to reserve space for + */ +#[inline(always)] +pub fn reserve_at_least(s: &mut ~str, n: uint) { + reserve(s, uint::next_power_of_two(n + 1u) - 1u) +} + +/** + * Returns the number of single-byte characters the string can hold without + * reallocating + */ +pub fn capacity(s: &const ~str) -> uint { + do as_bytes(s) |buf| { + let vcap = vec::capacity(buf); + assert!(vcap > 0u); + vcap - 1u + } +} + +/// Escape each char in `s` with char::escape_default. +pub fn escape_default(s: &str) -> ~str { + let mut out: ~str = ~""; + reserve_at_least(&mut out, str::len(s)); + for s.each_char |c| { + push_str(&mut out, char::escape_default(c)); + } + out +} + +/// Escape each char in `s` with char::escape_unicode. +pub fn escape_unicode(s: &str) -> ~str { + let mut out: ~str = ~""; + reserve_at_least(&mut out, str::len(s)); + for s.each_char |c| { + push_str(&mut out, char::escape_unicode(c)); + } + out +} + +/// Unsafe operations +pub mod raw { + use cast; + use libc; + use ptr; + use str::raw; + use str::{as_buf, is_utf8, len, reserve_at_least}; + use vec; + + /// Create a Rust string from a null-terminated *u8 buffer + pub unsafe fn from_buf(buf: *u8) -> ~str { + let mut curr = buf, i = 0u; + while *curr != 0u8 { + i += 1u; + curr = ptr::offset(buf, i); + } + return from_buf_len(buf, i); + } + + /// Create a Rust string from a *u8 buffer of the given length + pub unsafe fn from_buf_len(buf: *const u8, len: uint) -> ~str { + let mut v: ~[u8] = vec::with_capacity(len + 1); + vec::as_mut_buf(v, |vbuf, _len| { + ptr::copy_memory(vbuf, buf as *u8, len) + }); + vec::raw::set_len(&mut v, len); + v.push(0u8); + + assert!(is_utf8(v)); + return ::cast::transmute(v); + } + + /// Create a Rust string from a null-terminated C string + pub unsafe fn from_c_str(c_str: *libc::c_char) -> ~str { + from_buf(::cast::transmute(c_str)) + } + + /// Create a Rust string from a `*c_char` buffer of the given length + pub unsafe fn from_c_str_len(c_str: *libc::c_char, len: uint) -> ~str { + from_buf_len(::cast::transmute(c_str), len) + } + + /// Converts a vector of bytes to a new owned string. + pub unsafe fn from_bytes(v: &const [u8]) -> ~str { + do vec::as_const_buf(v) |buf, len| { + from_buf_len(buf, len) + } + } + + /// Converts a vector of bytes to a string. + /// The byte slice needs to contain valid utf8 and needs to be one byte longer than + /// the string, if possible ending in a 0 byte. + pub unsafe fn from_bytes_with_null<'a>(v: &'a [u8]) -> &'a str { + cast::transmute(v) + } + + /// Converts a byte to a string. + pub unsafe fn from_byte(u: u8) -> ~str { raw::from_bytes([u]) } + + /// Form a slice from a *u8 buffer of the given length without copying. + pub unsafe fn buf_as_slice<T>(buf: *u8, len: uint, + f: &fn(v: &str) -> T) -> T { + let v = (buf, len + 1); + assert!(is_utf8(::cast::transmute(v))); + f(::cast::transmute(v)) + } + + /** + * Takes a bytewise (not UTF-8) slice from a string. + * + * Returns the substring from [`begin`..`end`). + * + * # Failure + * + * If begin is greater than end. + * If end is greater than the length of the string. + */ + pub unsafe fn slice_bytes_owned(s: &str, begin: uint, end: uint) -> ~str { + do as_buf(s) |sbuf, n| { + assert!((begin <= end)); + assert!((end <= n)); + + let mut v = vec::with_capacity(end - begin + 1u); + do vec::as_imm_buf(v) |vbuf, _vlen| { + let vbuf = ::cast::transmute_mut_unsafe(vbuf); + let src = ptr::offset(sbuf, begin); + ptr::copy_memory(vbuf, src, end - begin); + } + vec::raw::set_len(&mut v, end - begin); + v.push(0u8); + ::cast::transmute(v) + } + } + + /** + * Takes a bytewise (not UTF-8) slice from a string. + * + * Returns the substring from [`begin`..`end`). + * + * # Failure + * + * If begin is greater than end. + * If end is greater than the length of the string. + */ + #[inline] + pub unsafe fn slice_bytes(s: &str, begin: uint, end: uint) -> &str { + do as_buf(s) |sbuf, n| { + assert!((begin <= end)); + assert!((end <= n)); + + let tuple = (ptr::offset(sbuf, begin), end - begin + 1); + ::cast::transmute(tuple) + } + } + + /// Appends a byte to a string. (Not UTF-8 safe). + pub unsafe fn push_byte(s: &mut ~str, b: u8) { + let new_len = s.len() + 1; + reserve_at_least(&mut *s, new_len); + do as_buf(*s) |buf, len| { + let buf: *mut u8 = ::cast::transmute(buf); + *ptr::mut_offset(buf, len) = b; + } + set_len(&mut *s, new_len); + } + + /// Appends a vector of bytes to a string. (Not UTF-8 safe). + unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) { + let new_len = s.len() + bytes.len(); + reserve_at_least(&mut *s, new_len); + for bytes.each |byte| { push_byte(&mut *s, *byte); } + } + + /// Removes the last byte from a string and returns it. (Not UTF-8 safe). + pub unsafe fn pop_byte(s: &mut ~str) -> u8 { + let len = len(*s); + assert!((len > 0u)); + let b = s[len - 1u]; + set_len(s, len - 1u); + return b; + } + + /// Removes the first byte from a string and returns it. (Not UTF-8 safe). + pub unsafe fn shift_byte(s: &mut ~str) -> u8 { + let len = len(*s); + assert!((len > 0u)); + let b = s[0]; + *s = raw::slice_bytes_owned(*s, 1u, len); + return b; + } + + /// Sets the length of the string and adds the null terminator + #[inline] + pub unsafe fn set_len(v: &mut ~str, new_len: uint) { + let v: **mut vec::raw::VecRepr = cast::transmute(v); + let repr: *mut vec::raw::VecRepr = *v; + (*repr).unboxed.fill = new_len + 1u; + let null = ptr::mut_offset(cast::transmute(&((*repr).unboxed.data)), + new_len); + *null = 0u8; + } + + #[test] + fn test_from_buf_len() { + unsafe { + let a = ~[65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; + let b = vec::raw::to_ptr(a); + let c = from_buf_len(b, 3u); + assert_eq!(c, ~"AAA"); + } + } + +} + +#[cfg(not(test))] +pub mod traits { + use ops::Add; + use str::append; + + impl<'self> Add<&'self str,~str> for ~str { + #[inline(always)] + fn add(&self, rhs: & &'self str) -> ~str { + append(copy *self, (*rhs)) + } + } +} + +#[cfg(test)] +pub mod traits {} + +pub trait StrSlice<'self> { + fn all(&self, it: &fn(char) -> bool) -> bool; + fn any(&self, it: &fn(char) -> bool) -> bool; + fn contains<'a>(&self, needle: &'a str) -> bool; + fn contains_char(&self, needle: char) -> bool; + fn char_iter(&self) -> StrCharIterator<'self>; + fn each(&self, it: &fn(u8) -> bool) -> bool; + fn eachi(&self, it: &fn(uint, u8) -> bool) -> bool; + fn each_reverse(&self, it: &fn(u8) -> bool) -> bool; + fn eachi_reverse(&self, it: &fn(uint, u8) -> bool) -> bool; + fn each_char(&self, it: &fn(char) -> bool) -> bool; + fn each_chari(&self, it: &fn(uint, char) -> bool) -> bool; + fn each_char_reverse(&self, it: &fn(char) -> bool) -> bool; + fn each_chari_reverse(&self, it: &fn(uint, char) -> bool) -> bool; + fn ends_with(&self, needle: &str) -> bool; + fn is_empty(&self) -> bool; + fn is_whitespace(&self) -> bool; + fn is_alphanumeric(&self) -> bool; + fn len(&self) -> uint; + fn char_len(&self) -> uint; + fn slice(&self, begin: uint, end: uint) -> &'self str; + fn each_split(&self, sepfn: &fn(char) -> bool, it: &fn(&'self str) -> bool) -> bool; + fn each_split_char(&self, sep: char, it: &fn(&'self str) -> bool) -> bool; + fn each_split_str<'a>(&self, sep: &'a str, it: &fn(&'self str) -> bool) -> bool; + fn starts_with<'a>(&self, needle: &'a str) -> bool; + fn substr(&self, begin: uint, n: uint) -> &'self str; + fn escape_default(&self) -> ~str; + fn escape_unicode(&self) -> ~str; + fn trim(&self) -> &'self str; + fn trim_left(&self) -> &'self str; + fn trim_right(&self) -> &'self str; + fn trim_chars(&self, chars_to_trim: &[char]) -> &'self str; + fn trim_left_chars(&self, chars_to_trim: &[char]) -> &'self str; + fn trim_right_chars(&self, chars_to_trim: &[char]) -> &'self str; + fn to_owned(&self) -> ~str; + fn to_managed(&self) -> @str; + fn char_at(&self, i: uint) -> char; + fn char_at_reverse(&self, i: uint) -> char; + fn to_bytes(&self) -> ~[u8]; +} + +/// Extension methods for strings +impl<'self> StrSlice<'self> for &'self str { + /** + * Return true if a predicate matches all characters or if the string + * contains no characters + */ + #[inline] + fn all(&self, it: &fn(char) -> bool) -> bool { all(*self, it) } + /** + * Return true if a predicate matches any character (and false if it + * matches none or there are no characters) + */ + #[inline] + fn any(&self, it: &fn(char) -> bool) -> bool { any(*self, it) } + /// Returns true if one string contains another + #[inline] + fn contains<'a>(&self, needle: &'a str) -> bool { + contains(*self, needle) + } + /// Returns true if a string contains a char + #[inline] + fn contains_char(&self, needle: char) -> bool { + contains_char(*self, needle) + } + + #[inline] + fn char_iter(&self) -> StrCharIterator<'self> { + StrCharIterator { + index: 0, + string: *self + } + } + + /// Iterate over the bytes in a string + #[inline] + fn each(&self, it: &fn(u8) -> bool) -> bool { each(*self, it) } + /// Iterate over the bytes in a string, with indices + #[inline] + fn eachi(&self, it: &fn(uint, u8) -> bool) -> bool { eachi(*self, it) } + /// Iterate over the bytes in a string + #[inline] + fn each_reverse(&self, it: &fn(u8) -> bool) -> bool { each_reverse(*self, it) } + /// Iterate over the bytes in a string, with indices + #[inline] + fn eachi_reverse(&self, it: &fn(uint, u8) -> bool) -> bool { + eachi_reverse(*self, it) + } + /// Iterate over the chars in a string + #[inline] + fn each_char(&self, it: &fn(char) -> bool) -> bool { each_char(*self, it) } + /// Iterate over the chars in a string, with indices + #[inline] + fn each_chari(&self, it: &fn(uint, char) -> bool) -> bool { + each_chari(*self, it) + } + /// Iterate over the chars in a string in reverse + #[inline] + fn each_char_reverse(&self, it: &fn(char) -> bool) -> bool { + each_char_reverse(*self, it) + } + /// Iterate over the chars in a string in reverse, with indices from the + /// end + #[inline] + fn each_chari_reverse(&self, it: &fn(uint, char) -> bool) -> bool { + each_chari_reverse(*self, it) + } + /// Returns true if one string ends with another + #[inline] + fn ends_with(&self, needle: &str) -> bool { + ends_with(*self, needle) + } + /// Returns true if the string has length 0 + #[inline] + fn is_empty(&self) -> bool { is_empty(*self) } + /** + * Returns true if the string contains only whitespace + * + * Whitespace characters are determined by `char::is_whitespace` + */ + #[inline] + fn is_whitespace(&self) -> bool { is_whitespace(*self) } + /** + * Returns true if the string contains only alphanumerics + * + * Alphanumeric characters are determined by `char::is_alphanumeric` + */ + #[inline] + fn is_alphanumeric(&self) -> bool { is_alphanumeric(*self) } + /// Returns the size in bytes not counting the null terminator + #[inline(always)] + fn len(&self) -> uint { len(*self) } + /// Returns the number of characters that a string holds + #[inline] + fn char_len(&self) -> uint { char_len(*self) } + /** + * Returns a slice of the given string from the byte range + * [`begin`..`end`) + * + * Fails when `begin` and `end` do not point to valid characters or + * beyond the last character of the string + */ + #[inline] + fn slice(&self, begin: uint, end: uint) -> &'self str { + slice(*self, begin, end) + } + /// Splits a string into substrings using a character function + #[inline] + fn each_split(&self, sepfn: &fn(char) -> bool, it: &fn(&'self str) -> bool) -> bool { + each_split(*self, sepfn, it) + } + /** + * Splits a string into substrings at each occurrence of a given character + */ + #[inline] + fn each_split_char(&self, sep: char, it: &fn(&'self str) -> bool) -> bool { + each_split_char(*self, sep, it) + } + /** + * Splits a string into a vector of the substrings separated by a given + * string + */ + #[inline] + fn each_split_str<'a>(&self, sep: &'a str, it: &fn(&'self str) -> bool) -> bool { + each_split_str(*self, sep, it) + } + /// Returns true if one string starts with another + #[inline] + fn starts_with<'a>(&self, needle: &'a str) -> bool { + starts_with(*self, needle) + } + /** + * Take a substring of another. + * + * Returns a string containing `n` characters starting at byte offset + * `begin`. + */ + #[inline] + fn substr(&self, begin: uint, n: uint) -> &'self str { + substr(*self, begin, n) + } + /// Escape each char in `s` with char::escape_default. + #[inline] + fn escape_default(&self) -> ~str { escape_default(*self) } + /// Escape each char in `s` with char::escape_unicode. + #[inline] + fn escape_unicode(&self) -> ~str { escape_unicode(*self) } + + /// Returns a string with leading and trailing whitespace removed + #[inline] + fn trim(&self) -> &'self str { trim(*self) } + /// Returns a string with leading whitespace removed + #[inline] + fn trim_left(&self) -> &'self str { trim_left(*self) } + /// Returns a string with trailing whitespace removed + #[inline] + fn trim_right(&self) -> &'self str { trim_right(*self) } + + #[inline] + fn trim_chars(&self, chars_to_trim: &[char]) -> &'self str { + trim_chars(*self, chars_to_trim) + } + #[inline] + fn trim_left_chars(&self, chars_to_trim: &[char]) -> &'self str { + trim_left_chars(*self, chars_to_trim) + } + #[inline] + fn trim_right_chars(&self, chars_to_trim: &[char]) -> &'self str { + trim_right_chars(*self, chars_to_trim) + } + + + #[inline] + fn to_owned(&self) -> ~str { to_owned(*self) } + + #[inline] + fn to_managed(&self) -> @str { + let v = at_vec::from_fn(self.len() + 1, |i| { + if i == self.len() { 0 } else { self[i] } + }); + unsafe { ::cast::transmute(v) } + } + + #[inline] + fn char_at(&self, i: uint) -> char { char_at(*self, i) } + + #[inline] + fn char_at_reverse(&self, i: uint) -> char { + char_at_reverse(*self, i) + } + + fn to_bytes(&self) -> ~[u8] { to_bytes(*self) } +} + +pub trait OwnedStr { + fn push_str(&mut self, v: &str); + fn push_char(&mut self, c: char); +} + +impl OwnedStr for ~str { + #[inline] + fn push_str(&mut self, v: &str) { + push_str(self, v); + } + #[inline] + fn push_char(&mut self, c: char) { + push_char(self, c); + } +} + +impl Clone for ~str { + #[inline(always)] + fn clone(&self) -> ~str { + to_owned(*self) + } +} + +pub struct StrCharIterator<'self> { + priv index: uint, + priv string: &'self str, +} + +impl<'self> Iterator<char> for StrCharIterator<'self> { + #[inline] + fn next(&mut self) -> Option<char> { + if self.index < self.string.len() { + let CharRange {ch, next} = char_range_at(self.string, self.index); + self.index = next; + Some(ch) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use container::Container; + use char; + use option::Some; + use libc::c_char; + use libc; + use old_iter::BaseIter; + use ptr; + use str::*; + use vec; + use vec::ImmutableVector; + use cmp::{TotalOrd, Less, Equal, Greater}; + + #[test] + fn test_eq() { + assert!((eq(&~"", &~""))); + assert!((eq(&~"foo", &~"foo"))); + assert!((!eq(&~"foo", &~"bar"))); + } + + #[test] + fn test_eq_slice() { + assert!((eq_slice(slice("foobar", 0, 3), "foo"))); + assert!((eq_slice(slice("barfoo", 3, 6), "foo"))); + assert!((!eq_slice("foo1", "foo2"))); + } + + #[test] + fn test_le() { + assert!((le(&"", &""))); + assert!((le(&"", &"foo"))); + assert!((le(&"foo", &"foo"))); + assert!((!eq(&~"foo", &~"bar"))); + } + + #[test] + fn test_len() { + assert_eq!(len(~""), 0u); + assert_eq!(len(~"hello world"), 11u); + assert_eq!(len(~"\x63"), 1u); + assert_eq!(len(~"\xa2"), 2u); + assert_eq!(len(~"\u03c0"), 2u); + assert_eq!(len(~"\u2620"), 3u); + assert_eq!(len(~"\U0001d11e"), 4u); + + assert_eq!(char_len(~""), 0u); + assert_eq!(char_len(~"hello world"), 11u); + assert_eq!(char_len(~"\x63"), 1u); + assert_eq!(char_len(~"\xa2"), 1u); + assert_eq!(char_len(~"\u03c0"), 1u); + assert_eq!(char_len(~"\u2620"), 1u); + assert_eq!(char_len(~"\U0001d11e"), 1u); + assert_eq!(char_len(~"ประเทศไทย中华Việt Nam"), 19u); + } + + #[test] + fn test_rfind_char() { + assert_eq!(rfind_char(~"hello", 'l'), Some(3u)); + assert_eq!(rfind_char(~"hello", 'o'), Some(4u)); + assert_eq!(rfind_char(~"hello", 'h'), Some(0u)); + assert!(rfind_char(~"hello", 'z').is_none()); + assert_eq!(rfind_char(~"ประเทศไทย中华Việt Nam", '华'), Some(30u)); + } + + #[test] + fn test_pop_char() { + let mut data = ~"ประเทศไทย中华"; + let cc = pop_char(&mut data); + assert_eq!(~"ประเทศไทย中", data); + assert_eq!('华', cc); + } + + #[test] + fn test_pop_char_2() { + let mut data2 = ~"华"; + let cc2 = pop_char(&mut data2); + assert_eq!(~"", data2); + assert_eq!('华', cc2); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_pop_char_fail() { + let mut data = ~""; + let _cc3 = pop_char(&mut data); + } + + #[test] + fn test_split_char() { + fn t(s: &str, c: char, u: &[~str]) { + debug!(~"split_byte: " + s); + let mut v = ~[]; + for each_split_char(s, c) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + assert!(vec::all2(v, u, |a,b| a == b)); + } + t(~"abc.hello.there", '.', ~[~"abc", ~"hello", ~"there"]); + t(~".hello.there", '.', ~[~"", ~"hello", ~"there"]); + t(~"...hello.there.", '.', ~[~"", ~"", ~"", ~"hello", ~"there", ~""]); + + t(~"", 'z', ~[~""]); + t(~"z", 'z', ~[~"",~""]); + t(~"ok", 'z', ~[~"ok"]); + } + + #[test] + fn test_split_char_2() { + fn t(s: &str, c: char, u: &[~str]) { + debug!(~"split_byte: " + s); + let mut v = ~[]; + for each_split_char(s, c) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + assert!(vec::all2(v, u, |a,b| a == b)); + } + let data = ~"ประเทศไทย中华Việt Nam"; + t(data, 'V', ~[~"ประเทศไทย中华", ~"iệt Nam"]); + t(data, 'ท', ~[~"ประเ", ~"ศไ", ~"ย中华Việt Nam"]); + } + + #[test] + fn test_splitn_char() { + fn t(s: &str, c: char, n: uint, u: &[~str]) { + debug!(~"splitn_byte: " + s); + let mut v = ~[]; + for each_splitn_char(s, c, n) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + debug!("comparing vs. %?", u); + assert!(vec::all2(v, u, |a,b| a == b)); + } + t(~"abc.hello.there", '.', 0u, ~[~"abc.hello.there"]); + t(~"abc.hello.there", '.', 1u, ~[~"abc", ~"hello.there"]); + t(~"abc.hello.there", '.', 2u, ~[~"abc", ~"hello", ~"there"]); + t(~"abc.hello.there", '.', 3u, ~[~"abc", ~"hello", ~"there"]); + t(~".hello.there", '.', 0u, ~[~".hello.there"]); + t(~".hello.there", '.', 1u, ~[~"", ~"hello.there"]); + t(~"...hello.there.", '.', 3u, ~[~"", ~"", ~"", ~"hello.there."]); + t(~"...hello.there.", '.', 5u, ~[~"", ~"", ~"", ~"hello", ~"there", ~""]); + + t(~"", 'z', 5u, ~[~""]); + t(~"z", 'z', 5u, ~[~"",~""]); + t(~"ok", 'z', 5u, ~[~"ok"]); + t(~"z", 'z', 0u, ~[~"z"]); + t(~"w.x.y", '.', 0u, ~[~"w.x.y"]); + t(~"w.x.y", '.', 1u, ~[~"w",~"x.y"]); + } + + #[test] + fn test_splitn_char_2 () { + fn t(s: &str, c: char, n: uint, u: &[~str]) { + debug!(~"splitn_byte: " + s); + let mut v = ~[]; + for each_splitn_char(s, c, n) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + debug!("comparing vs. %?", u); + assert!(vec::all2(v, u, |a,b| a == b)); + } + + t(~"ประเทศไทย中华Việt Nam", '华', 1u, ~[~"ประเทศไทย中", ~"Việt Nam"]); + t(~"zzXXXzYYYzWWWz", 'z', 3u, ~[~"", ~"", ~"XXX", ~"YYYzWWWz"]); + t(~"z", 'z', 5u, ~[~"",~""]); + t(~"", 'z', 5u, ~[~""]); + t(~"ok", 'z', 5u, ~[~"ok"]); + } + + + #[test] + fn test_splitn_char_3() { + fn t(s: &str, c: char, n: uint, u: &[~str]) { + debug!(~"splitn_byte: " + s); + let mut v = ~[]; + for each_splitn_char(s, c, n) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + debug!("comparing vs. %?", u); + assert!(vec::all2(v, u, |a,b| a == b)); + } + let data = ~"ประเทศไทย中华Việt Nam"; + t(data, 'V', 1u, ~[~"ประเทศไทย中华", ~"iệt Nam"]); + t(data, 'ท', 1u, ~[~"ประเ", ~"ศไทย中华Việt Nam"]); + } + + #[test] + fn test_split_char_no_trailing() { + fn t(s: &str, c: char, u: &[~str]) { + debug!(~"split_byte: " + s); + let mut v = ~[]; + for each_split_char_no_trailing(s, c) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + assert!(vec::all2(v, u, |a,b| a == b)); + } + t(~"abc.hello.there", '.', ~[~"abc", ~"hello", ~"there"]); + t(~".hello.there", '.', ~[~"", ~"hello", ~"there"]); + t(~"...hello.there.", '.', ~[~"", ~"", ~"", ~"hello", ~"there"]); + + t(~"...hello.there.", '.', ~[~"", ~"", ~"", ~"hello", ~"there"]); + t(~"", 'z', ~[]); + t(~"z", 'z', ~[~""]); + t(~"ok", 'z', ~[~"ok"]); + } + + #[test] + fn test_split_char_no_trailing_2() { + fn t(s: &str, c: char, u: &[~str]) { + debug!(~"split_byte: " + s); + let mut v = ~[]; + for each_split_char_no_trailing(s, c) |s| { v.push(s.to_owned()) } + debug!("split_byte to: %?", v); + assert!(vec::all2(v, u, |a,b| a == b)); + } + let data = ~"ประเทศไทย中华Việt Nam"; + t(data, 'V', ~[~"ประเทศไทย中华", ~"iệt Nam"]); + t(data, 'ท', ~[~"ประเ", ~"ศไ", ~"ย中华Việt Nam"]); + } + + #[test] + fn test_split_str() { + fn t<'a>(s: &str, sep: &'a str, u: &[~str]) { + let mut v = ~[]; + for each_split_str(s, sep) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + t(~"--1233345--", ~"12345", ~[~"--1233345--"]); + t(~"abc::hello::there", ~"::", ~[~"abc", ~"hello", ~"there"]); + t(~"::hello::there", ~"::", ~[~"", ~"hello", ~"there"]); + t(~"hello::there::", ~"::", ~[~"hello", ~"there", ~""]); + t(~"::hello::there::", ~"::", ~[~"", ~"hello", ~"there", ~""]); + t(~"ประเทศไทย中华Việt Nam", ~"中华", ~[~"ประเทศไทย", ~"Việt Nam"]); + t(~"zzXXXzzYYYzz", ~"zz", ~[~"", ~"XXX", ~"YYY", ~""]); + t(~"zzXXXzYYYz", ~"XXX", ~[~"zz", ~"zYYYz"]); + t(~".XXX.YYY.", ~".", ~[~"", ~"XXX", ~"YYY", ~""]); + t(~"", ~".", ~[~""]); + t(~"zz", ~"zz", ~[~"",~""]); + t(~"ok", ~"z", ~[~"ok"]); + t(~"zzz", ~"zz", ~[~"",~"z"]); + t(~"zzzzz", ~"zz", ~[~"",~"",~"z"]); + } + + + #[test] + fn test_split() { + fn t(s: &str, sepf: &fn(char) -> bool, u: &[~str]) { + let mut v = ~[]; + for each_split(s, sepf) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + + t(~"ประเทศไทย中华Việt Nam", |cc| cc == '华', ~[~"ประเทศไทย中", ~"Việt Nam"]); + t(~"zzXXXzYYYz", char::is_lowercase, ~[~"", ~"", ~"XXX", ~"YYY", ~""]); + t(~"zzXXXzYYYz", char::is_uppercase, ~[~"zz", ~"", ~"", ~"z", ~"", ~"", ~"z"]); + t(~"z", |cc| cc == 'z', ~[~"",~""]); + t(~"", |cc| cc == 'z', ~[~""]); + t(~"ok", |cc| cc == 'z', ~[~"ok"]); + } + + #[test] + fn test_split_no_trailing() { + fn t(s: &str, sepf: &fn(char) -> bool, u: &[~str]) { + let mut v = ~[]; + for each_split_no_trailing(s, sepf) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + + t(~"ประเทศไทย中华Việt Nam", |cc| cc == '华', ~[~"ประเทศไทย中", ~"Việt Nam"]); + t(~"zzXXXzYYYz", char::is_lowercase, ~[~"", ~"", ~"XXX", ~"YYY"]); + t(~"zzXXXzYYYz", char::is_uppercase, ~[~"zz", ~"", ~"", ~"z", ~"", ~"", ~"z"]); + t(~"z", |cc| cc == 'z', ~[~""]); + t(~"", |cc| cc == 'z', ~[]); + t(~"ok", |cc| cc == 'z', ~[~"ok"]); + } + + #[test] + fn test_lines() { + let lf = ~"\nMary had a little lamb\nLittle lamb\n"; + let crlf = ~"\r\nMary had a little lamb\r\nLittle lamb\r\n"; + + fn t(s: &str, f: &fn(&str, &fn(&str) -> bool) -> bool, u: &[~str]) { + let mut v = ~[]; + for f(s) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + + t(lf, each_line ,~[~"", ~"Mary had a little lamb", ~"Little lamb"]); + t(lf, each_line_any, ~[~"", ~"Mary had a little lamb", ~"Little lamb"]); + t(crlf, each_line, ~[~"\r", ~"Mary had a little lamb\r", ~"Little lamb\r"]); + t(crlf, each_line_any, ~[~"", ~"Mary had a little lamb", ~"Little lamb"]); + t(~"", each_line, ~[]); + t(~"", each_line_any, ~[]); + t(~"\n", each_line, ~[~""]); + t(~"\n", each_line_any, ~[~""]); + t(~"banana", each_line, ~[~"banana"]); + t(~"banana", each_line_any, ~[~"banana"]); + } + + #[test] + fn test_words () { + fn t(s: &str, f: &fn(&str, &fn(&str) -> bool) -> bool, u: &[~str]) { + let mut v = ~[]; + for f(s) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + let data = ~"\nMary had a little lamb\nLittle lamb\n"; + + t(data, each_word, ~[~"Mary",~"had",~"a",~"little",~"lamb",~"Little",~"lamb"]); + t(~"ok", each_word, ~[~"ok"]); + t(~"", each_word, ~[]); + } + + #[test] + fn test_split_within() { + fn t(s: &str, i: uint, u: &[~str]) { + let mut v = ~[]; + for each_split_within(s, i) |s| { v.push(s.to_owned()) } + assert!(vec::all2(v, u, |a,b| a == b)); + } + t(~"", 0, ~[]); + t(~"", 15, ~[]); + t(~"hello", 15, ~[~"hello"]); + t(~"\nMary had a little lamb\nLittle lamb\n", 15, + ~[~"Mary had a", ~"little lamb", ~"Little lamb"]); + } + + #[test] + fn test_find_str() { + // byte positions + assert!(find_str(~"banana", ~"apple pie").is_none()); + assert_eq!(find_str(~"", ~""), Some(0u)); + + let data = ~"ประเทศไทย中华Việt Nam"; + assert_eq!(find_str(data, ~""), Some(0u)); + assert_eq!(find_str(data, ~"ประเ"), Some( 0u)); + assert_eq!(find_str(data, ~"ะเ"), Some( 6u)); + assert_eq!(find_str(data, ~"中华"), Some(27u)); + assert!(find_str(data, ~"ไท华").is_none()); + } + + #[test] + fn test_find_str_between() { + // byte positions + assert_eq!(find_str_between(~"", ~"", 0u, 0u), Some(0u)); + + let data = ~"abcabc"; + assert_eq!(find_str_between(data, ~"ab", 0u, 6u), Some(0u)); + assert_eq!(find_str_between(data, ~"ab", 2u, 6u), Some(3u)); + assert!(find_str_between(data, ~"ab", 2u, 4u).is_none()); + + let mut data = ~"ประเทศไทย中华Việt Nam"; + data = data + data; + assert_eq!(find_str_between(data, ~"", 0u, 43u), Some(0u)); + assert_eq!(find_str_between(data, ~"", 6u, 43u), Some(6u)); + + assert_eq!(find_str_between(data, ~"ประ", 0u, 43u), Some( 0u)); + assert_eq!(find_str_between(data, ~"ทศไ", 0u, 43u), Some(12u)); + assert_eq!(find_str_between(data, ~"ย中", 0u, 43u), Some(24u)); + assert_eq!(find_str_between(data, ~"iệt", 0u, 43u), Some(34u)); + assert_eq!(find_str_between(data, ~"Nam", 0u, 43u), Some(40u)); + + assert_eq!(find_str_between(data, ~"ประ", 43u, 86u), Some(43u)); + assert_eq!(find_str_between(data, ~"ทศไ", 43u, 86u), Some(55u)); + assert_eq!(find_str_between(data, ~"ย中", 43u, 86u), Some(67u)); + assert_eq!(find_str_between(data, ~"iệt", 43u, 86u), Some(77u)); + assert_eq!(find_str_between(data, ~"Nam", 43u, 86u), Some(83u)); + } + + #[test] + fn test_substr() { + fn t(a: &str, b: &str, start: int) { + assert_eq!(substr(a, start as uint, len(b)), b); + } + t("hello", "llo", 2); + t("hello", "el", 1); + assert_eq!("ะเทศไท", substr("ประเทศไทย中华Việt Nam", 6u, 6u)); + } + + #[test] + fn test_concat() { + fn t(v: &[~str], s: &str) { + assert_eq!(concat(v), s.to_str()); + } + t(~[~"you", ~"know", ~"I'm", ~"no", ~"good"], ~"youknowI'mnogood"); + let v: ~[~str] = ~[]; + t(v, ~""); + t(~[~"hi"], ~"hi"); + } + + #[test] + fn test_connect() { + fn t(v: &[~str], sep: &str, s: &str) { + assert_eq!(connect(v, sep), s.to_str()); + } + t(~[~"you", ~"know", ~"I'm", ~"no", ~"good"], + ~" ", ~"you know I'm no good"); + let v: ~[~str] = ~[]; + t(v, ~" ", ~""); + t(~[~"hi"], ~" ", ~"hi"); + } + + #[test] + fn test_connect_slices() { + fn t(v: &[&str], sep: &str, s: &str) { + assert_eq!(connect_slices(v, sep), s.to_str()); + } + t(["you", "know", "I'm", "no", "good"], + " ", "you know I'm no good"); + t([], " ", ""); + t(["hi"], " ", "hi"); + } + + #[test] + fn test_repeat() { + assert_eq!(repeat(~"x", 4), ~"xxxx"); + assert_eq!(repeat(~"hi", 4), ~"hihihihi"); + assert_eq!(repeat(~"ไท华", 3), ~"ไท华ไท华ไท华"); + assert_eq!(repeat(~"", 4), ~""); + assert_eq!(repeat(~"hi", 0), ~""); + } + + #[test] + fn test_unsafe_slice() { + assert_eq!("ab", unsafe {raw::slice_bytes("abc", 0, 2)}); + assert_eq!("bc", unsafe {raw::slice_bytes("abc", 1, 3)}); + assert_eq!("", unsafe {raw::slice_bytes("abc", 1, 1)}); + fn a_million_letter_a() -> ~str { + let mut i = 0; + let mut rs = ~""; + while i < 100000 { push_str(&mut rs, "aaaaaaaaaa"); i += 1; } + rs + } + fn half_a_million_letter_a() -> ~str { + let mut i = 0; + let mut rs = ~""; + while i < 100000 { push_str(&mut rs, "aaaaa"); i += 1; } + rs + } + let letters = a_million_letter_a(); + assert!(half_a_million_letter_a() == + unsafe {raw::slice_bytes(letters, 0u, 500000)}.to_owned()); + } + + #[test] + fn test_starts_with() { + assert!((starts_with(~"", ~""))); + assert!((starts_with(~"abc", ~""))); + assert!((starts_with(~"abc", ~"a"))); + assert!((!starts_with(~"a", ~"abc"))); + assert!((!starts_with(~"", ~"abc"))); + } + + #[test] + fn test_ends_with() { + assert!((ends_with(~"", ~""))); + assert!((ends_with(~"abc", ~""))); + assert!((ends_with(~"abc", ~"c"))); + assert!((!ends_with(~"a", ~"abc"))); + assert!((!ends_with(~"", ~"abc"))); + } + + #[test] + fn test_is_empty() { + assert!((is_empty(~""))); + assert!((!is_empty(~"a"))); + } + + #[test] + fn test_replace() { + let a = ~"a"; + assert_eq!(replace(~"", a, ~"b"), ~""); + assert_eq!(replace(~"a", a, ~"b"), ~"b"); + assert_eq!(replace(~"ab", a, ~"b"), ~"bb"); + let test = ~"test"; + assert!(replace(~" test test ", test, ~"toast") == + ~" toast toast "); + assert_eq!(replace(~" test test ", test, ~""), ~" "); + } + + #[test] + fn test_replace_2a() { + let data = ~"ประเทศไทย中华"; + let repl = ~"دولة الكويت"; + + let a = ~"ประเ"; + let A = ~"دولة الكويتทศไทย中华"; + assert_eq!(replace(data, a, repl), A); + } + + #[test] + fn test_replace_2b() { + let data = ~"ประเทศไทย中华"; + let repl = ~"دولة الكويت"; + + let b = ~"ะเ"; + let B = ~"ปรدولة الكويتทศไทย中华"; + assert_eq!(replace(data, b, repl), B); + } + + #[test] + fn test_replace_2c() { + let data = ~"ประเทศไทย中华"; + let repl = ~"دولة الكويت"; + + let c = ~"中华"; + let C = ~"ประเทศไทยدولة الكويت"; + assert_eq!(replace(data, c, repl), C); + } + + #[test] + fn test_replace_2d() { + let data = ~"ประเทศไทย中华"; + let repl = ~"دولة الكويت"; + + let d = ~"ไท华"; + assert_eq!(replace(data, d, repl), data); + } + + #[test] + fn test_slice() { + assert_eq!("ab", slice("abc", 0, 2)); + assert_eq!("bc", slice("abc", 1, 3)); + assert_eq!("", slice("abc", 1, 1)); + assert_eq!("\u65e5", slice("\u65e5\u672c", 0, 3)); + + let data = "ประเทศไทย中华"; + assert_eq!("ป", slice(data, 0, 3)); + assert_eq!("ร", slice(data, 3, 6)); + assert_eq!("", slice(data, 3, 3)); + assert_eq!("华", slice(data, 30, 33)); + + fn a_million_letter_X() -> ~str { + let mut i = 0; + let mut rs = ~""; + while i < 100000 { + push_str(&mut rs, "华华华华华华华华华华"); + i += 1; + } + rs + } + fn half_a_million_letter_X() -> ~str { + let mut i = 0; + let mut rs = ~""; + while i < 100000 { push_str(&mut rs, "华华华华华"); i += 1; } + rs + } + let letters = a_million_letter_X(); + assert!(half_a_million_letter_X() == + slice(letters, 0u, 3u * 500000u).to_owned()); + } + + #[test] + fn test_slice_2() { + let ss = "中华Việt Nam"; + + assert_eq!("华", slice(ss, 3u, 6u)); + assert_eq!("Việt Nam", slice(ss, 6u, 16u)); + + assert_eq!("ab", slice("abc", 0u, 2u)); + assert_eq!("bc", slice("abc", 1u, 3u)); + assert_eq!("", slice("abc", 1u, 1u)); + + assert_eq!("中", slice(ss, 0u, 3u)); + assert_eq!("华V", slice(ss, 3u, 7u)); + assert_eq!("", slice(ss, 3u, 3u)); + /*0: 中 + 3: 华 + 6: V + 7: i + 8: ệ + 11: t + 12: + 13: N + 14: a + 15: m */ + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_slice_fail() { + slice("中华Việt Nam", 0u, 2u); + } + + #[test] + fn test_trim_left_chars() { + assert!(trim_left_chars(" *** foo *** ", ~[]) == + " *** foo *** "); + assert!(trim_left_chars(" *** foo *** ", ~['*', ' ']) == + "foo *** "); + assert_eq!(trim_left_chars(" *** *** ", ~['*', ' ']), ""); + assert!(trim_left_chars("foo *** ", ~['*', ' ']) == + "foo *** "); + } + + #[test] + fn test_trim_right_chars() { + assert!(trim_right_chars(" *** foo *** ", ~[]) == + " *** foo *** "); + assert!(trim_right_chars(" *** foo *** ", ~['*', ' ']) == + " *** foo"); + assert_eq!(trim_right_chars(" *** *** ", ~['*', ' ']), ""); + assert!(trim_right_chars(" *** foo", ~['*', ' ']) == + " *** foo"); + } + + #[test] + fn test_trim_chars() { + assert_eq!(trim_chars(" *** foo *** ", ~[]), " *** foo *** "); + assert_eq!(trim_chars(" *** foo *** ", ~['*', ' ']), "foo"); + assert_eq!(trim_chars(" *** *** ", ~['*', ' ']), ""); + assert_eq!(trim_chars("foo", ~['*', ' ']), "foo"); + } + + #[test] + fn test_trim_left() { + assert_eq!(trim_left(""), ""); + assert_eq!(trim_left("a"), "a"); + assert_eq!(trim_left(" "), ""); + assert_eq!(trim_left(" blah"), "blah"); + assert_eq!(trim_left(" \u3000 wut"), "wut"); + assert_eq!(trim_left("hey "), "hey "); + } + + #[test] + fn test_trim_right() { + assert_eq!(trim_right(""), ""); + assert_eq!(trim_right("a"), "a"); + assert_eq!(trim_right(" "), ""); + assert_eq!(trim_right("blah "), "blah"); + assert_eq!(trim_right("wut \u3000 "), "wut"); + assert_eq!(trim_right(" hey"), " hey"); + } + + #[test] + fn test_trim() { + assert_eq!(trim(""), ""); + assert_eq!(trim("a"), "a"); + assert_eq!(trim(" "), ""); + assert_eq!(trim(" blah "), "blah"); + assert_eq!(trim("\nwut \u3000 "), "wut"); + assert_eq!(trim(" hey dude "), "hey dude"); + } + + #[test] + fn test_is_whitespace() { + assert!((is_whitespace(~""))); + assert!((is_whitespace(~" "))); + assert!((is_whitespace(~"\u2009"))); // Thin space + assert!((is_whitespace(~" \n\t "))); + assert!((!is_whitespace(~" _ "))); + } + + #[test] + fn test_shift_byte() { + let mut s = ~"ABC"; + let b = unsafe{raw::shift_byte(&mut s)}; + assert_eq!(s, ~"BC"); + assert_eq!(b, 65u8); + } + + #[test] + fn test_pop_byte() { + let mut s = ~"ABC"; + let b = unsafe{raw::pop_byte(&mut s)}; + assert_eq!(s, ~"AB"); + assert_eq!(b, 67u8); + } + + #[test] + fn test_unsafe_from_bytes() { + let a = ~[65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8]; + let b = unsafe { raw::from_bytes(a) }; + assert_eq!(b, ~"AAAAAAA"); + } + + #[test] + fn test_from_bytes() { + let ss = ~"ศไทย中华Việt Nam"; + let bb = ~[0xe0_u8, 0xb8_u8, 0xa8_u8, + 0xe0_u8, 0xb9_u8, 0x84_u8, + 0xe0_u8, 0xb8_u8, 0x97_u8, + 0xe0_u8, 0xb8_u8, 0xa2_u8, + 0xe4_u8, 0xb8_u8, 0xad_u8, + 0xe5_u8, 0x8d_u8, 0x8e_u8, + 0x56_u8, 0x69_u8, 0xe1_u8, + 0xbb_u8, 0x87_u8, 0x74_u8, + 0x20_u8, 0x4e_u8, 0x61_u8, + 0x6d_u8]; + + assert_eq!(ss, from_bytes(bb)); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_from_bytes_fail() { + let bb = ~[0xff_u8, 0xb8_u8, 0xa8_u8, + 0xe0_u8, 0xb9_u8, 0x84_u8, + 0xe0_u8, 0xb8_u8, 0x97_u8, + 0xe0_u8, 0xb8_u8, 0xa2_u8, + 0xe4_u8, 0xb8_u8, 0xad_u8, + 0xe5_u8, 0x8d_u8, 0x8e_u8, + 0x56_u8, 0x69_u8, 0xe1_u8, + 0xbb_u8, 0x87_u8, 0x74_u8, + 0x20_u8, 0x4e_u8, 0x61_u8, + 0x6d_u8]; + + let _x = from_bytes(bb); + } + + #[test] + fn test_unsafe_from_bytes_with_null() { + let a = [65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; + let b = unsafe { raw::from_bytes_with_null(a) }; + assert_eq!(b, "AAAAAAA"); + } + + #[test] + fn test_from_bytes_with_null() { + let ss = "ศไทย中华Việt Nam"; + let bb = [0xe0_u8, 0xb8_u8, 0xa8_u8, + 0xe0_u8, 0xb9_u8, 0x84_u8, + 0xe0_u8, 0xb8_u8, 0x97_u8, + 0xe0_u8, 0xb8_u8, 0xa2_u8, + 0xe4_u8, 0xb8_u8, 0xad_u8, + 0xe5_u8, 0x8d_u8, 0x8e_u8, + 0x56_u8, 0x69_u8, 0xe1_u8, + 0xbb_u8, 0x87_u8, 0x74_u8, + 0x20_u8, 0x4e_u8, 0x61_u8, + 0x6d_u8, 0x0_u8]; + + assert_eq!(ss, from_bytes_with_null(bb)); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_from_bytes_with_null_fail() { + let bb = [0xff_u8, 0xb8_u8, 0xa8_u8, + 0xe0_u8, 0xb9_u8, 0x84_u8, + 0xe0_u8, 0xb8_u8, 0x97_u8, + 0xe0_u8, 0xb8_u8, 0xa2_u8, + 0xe4_u8, 0xb8_u8, 0xad_u8, + 0xe5_u8, 0x8d_u8, 0x8e_u8, + 0x56_u8, 0x69_u8, 0xe1_u8, + 0xbb_u8, 0x87_u8, 0x74_u8, + 0x20_u8, 0x4e_u8, 0x61_u8, + 0x6d_u8, 0x0_u8]; + + let _x = from_bytes_with_null(bb); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_from_bytes_with_null_fail_2() { + let bb = [0xff_u8, 0xb8_u8, 0xa8_u8, + 0xe0_u8, 0xb9_u8, 0x84_u8, + 0xe0_u8, 0xb8_u8, 0x97_u8, + 0xe0_u8, 0xb8_u8, 0xa2_u8, + 0xe4_u8, 0xb8_u8, 0xad_u8, + 0xe5_u8, 0x8d_u8, 0x8e_u8, + 0x56_u8, 0x69_u8, 0xe1_u8, + 0xbb_u8, 0x87_u8, 0x74_u8, + 0x20_u8, 0x4e_u8, 0x61_u8, + 0x6d_u8, 0x60_u8]; + + let _x = from_bytes_with_null(bb); + } + + #[test] + fn test_from_buf() { + unsafe { + let a = ~[65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; + let b = vec::raw::to_ptr(a); + let c = raw::from_buf(b); + assert_eq!(c, ~"AAAAAAA"); + } + } + + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_as_bytes_fail() { + // Don't double free + as_bytes::<()>(&~"", |_bytes| fail!() ); + } + + #[test] + fn test_as_buf() { + let a = ~"Abcdefg"; + let b = as_buf(a, |buf, _l| { + assert_eq!(unsafe { *buf }, 65u8); + 100 + }); + assert_eq!(b, 100); + } + + #[test] + fn test_as_buf_small() { + let a = ~"A"; + let b = as_buf(a, |buf, _l| { + assert_eq!(unsafe { *buf }, 65u8); + 100 + }); + assert_eq!(b, 100); + } + + #[test] + fn test_as_buf2() { + unsafe { + let s = ~"hello"; + let sb = as_buf(s, |b, _l| b); + let s_cstr = raw::from_buf(sb); + assert_eq!(s_cstr, s); + } + } + + #[test] + fn test_as_buf_3() { + let a = ~"hello"; + do as_buf(a) |buf, len| { + unsafe { + assert_eq!(a[0], 'h' as u8); + assert_eq!(*buf, 'h' as u8); + assert_eq!(len, 6u); + assert_eq!(*ptr::offset(buf,4u), 'o' as u8); + assert_eq!(*ptr::offset(buf,5u), 0u8); + } + } + } + + #[test] + fn test_subslice_offset() { + let a = "kernelsprite"; + let b = slice(a, 7, len(a)); + let c = slice(a, 0, len(a) - 6); + assert_eq!(subslice_offset(a, b), 7); + assert_eq!(subslice_offset(a, c), 0); + + let string = "a\nb\nc"; + let mut lines = ~[]; + for each_line(string) |line| { lines.push(line) } + assert_eq!(subslice_offset(string, lines[0]), 0); + assert_eq!(subslice_offset(string, lines[1]), 2); + assert_eq!(subslice_offset(string, lines[2]), 4); + } + + #[test] + #[should_fail] + fn test_subslice_offset_2() { + let a = "alchemiter"; + let b = "cruxtruder"; + subslice_offset(a, b); + } + + #[test] + fn vec_str_conversions() { + let s1: ~str = ~"All mimsy were the borogoves"; + + let v: ~[u8] = to_bytes(s1); + let s2: ~str = from_bytes(v); + let mut i: uint = 0u; + let n1: uint = len(s1); + let n2: uint = vec::len::<u8>(v); + assert_eq!(n1, n2); + while i < n1 { + let a: u8 = s1[i]; + let b: u8 = s2[i]; + debug!(a); + debug!(b); + assert_eq!(a, b); + i += 1u; + } + } + + #[test] + fn test_contains() { + assert!(contains(~"abcde", ~"bcd")); + assert!(contains(~"abcde", ~"abcd")); + assert!(contains(~"abcde", ~"bcde")); + assert!(contains(~"abcde", ~"")); + assert!(contains(~"", ~"")); + assert!(!contains(~"abcde", ~"def")); + assert!(!contains(~"", ~"a")); + + let data = ~"ประเทศไทย中华Việt Nam"; + assert!(contains(data, ~"ประเ")); + assert!(contains(data, ~"ะเ")); + assert!(contains(data, ~"中华")); + assert!(!contains(data, ~"ไท华")); + } + + #[test] + fn test_contains_char() { + assert!(contains_char(~"abc", 'b')); + assert!(contains_char(~"a", 'a')); + assert!(!contains_char(~"abc", 'd')); + assert!(!contains_char(~"", 'a')); + } + + #[test] + fn test_split_char_each() { + let data = ~"\nMary had a little lamb\nLittle lamb\n"; + + let mut ii = 0; + + for each_split_char(data, ' ') |xx| { + match ii { + 0 => assert!("\nMary" == xx), + 1 => assert!("had" == xx), + 2 => assert!("a" == xx), + 3 => assert!("little" == xx), + _ => () + } + ii += 1; + } + } + + #[test] + fn test_splitn_char_each() { + let data = ~"\nMary had a little lamb\nLittle lamb\n"; + + let mut ii = 0; + + for each_splitn_char(data, ' ', 2u) |xx| { + match ii { + 0 => assert!("\nMary" == xx), + 1 => assert!("had" == xx), + 2 => assert!("a little lamb\nLittle lamb\n" == xx), + _ => () + } + ii += 1; + } + } + + #[test] + fn test_words_each() { + let data = ~"\nMary had a little lamb\nLittle lamb\n"; + + let mut ii = 0; + + for each_word(data) |ww| { + match ii { + 0 => assert!("Mary" == ww), + 1 => assert!("had" == ww), + 2 => assert!("a" == ww), + 3 => assert!("little" == ww), + _ => () + } + ii += 1; + } + + each_word(~"", |_x| fail!()); // should not fail + } + + #[test] + fn test_lines_each () { + let lf = ~"\nMary had a little lamb\nLittle lamb\n"; + + let mut ii = 0; + + for each_line(lf) |x| { + match ii { + 0 => assert!("" == x), + 1 => assert!("Mary had a little lamb" == x), + 2 => assert!("Little lamb" == x), + _ => () + } + ii += 1; + } + } + + #[test] + fn test_map() { + assert_eq!(~"", map(~"", |c| unsafe {libc::toupper(c as c_char)} as char)); + assert_eq!(~"YMCA", map(~"ymca", |c| unsafe {libc::toupper(c as c_char)} as char)); + } + + #[test] + fn test_all() { + assert_eq!(true, all(~"", char::is_uppercase)); + assert_eq!(false, all(~"ymca", char::is_uppercase)); + assert_eq!(true, all(~"YMCA", char::is_uppercase)); + assert_eq!(false, all(~"yMCA", char::is_uppercase)); + assert_eq!(false, all(~"YMCy", char::is_uppercase)); + } + + #[test] + fn test_any() { + assert_eq!(false, any(~"", char::is_uppercase)); + assert_eq!(false, any(~"ymca", char::is_uppercase)); + assert_eq!(true, any(~"YMCA", char::is_uppercase)); + assert_eq!(true, any(~"yMCA", char::is_uppercase)); + assert_eq!(true, any(~"Ymcy", char::is_uppercase)); + } + + #[test] + fn test_chars() { + let ss = ~"ศไทย中华Việt Nam"; + assert!(~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a', + 'm'] + == to_chars(ss)); + } + + #[test] + fn test_utf16() { + let pairs = + ~[(~"𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n", + ~[0xd800_u16, 0xdf45_u16, 0xd800_u16, 0xdf3f_u16, + 0xd800_u16, 0xdf3b_u16, 0xd800_u16, 0xdf46_u16, + 0xd800_u16, 0xdf39_u16, 0xd800_u16, 0xdf3b_u16, + 0xd800_u16, 0xdf30_u16, 0x000a_u16]), + + (~"𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n", + ~[0xd801_u16, 0xdc12_u16, 0xd801_u16, + 0xdc49_u16, 0xd801_u16, 0xdc2e_u16, 0xd801_u16, + 0xdc40_u16, 0xd801_u16, 0xdc32_u16, 0xd801_u16, + 0xdc4b_u16, 0x0020_u16, 0xd801_u16, 0xdc0f_u16, + 0xd801_u16, 0xdc32_u16, 0xd801_u16, 0xdc4d_u16, + 0x000a_u16]), + + (~"𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n", + ~[0xd800_u16, 0xdf00_u16, 0xd800_u16, 0xdf16_u16, + 0xd800_u16, 0xdf0b_u16, 0xd800_u16, 0xdf04_u16, + 0xd800_u16, 0xdf11_u16, 0xd800_u16, 0xdf09_u16, + 0x00b7_u16, 0xd800_u16, 0xdf0c_u16, 0xd800_u16, + 0xdf04_u16, 0xd800_u16, 0xdf15_u16, 0xd800_u16, + 0xdf04_u16, 0xd800_u16, 0xdf0b_u16, 0xd800_u16, + 0xdf09_u16, 0xd800_u16, 0xdf11_u16, 0x000a_u16 ]), + + (~"𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n", + ~[0xd801_u16, 0xdc8b_u16, 0xd801_u16, 0xdc98_u16, + 0xd801_u16, 0xdc88_u16, 0xd801_u16, 0xdc91_u16, + 0xd801_u16, 0xdc9b_u16, 0xd801_u16, 0xdc92_u16, + 0x0020_u16, 0xd801_u16, 0xdc95_u16, 0xd801_u16, + 0xdc93_u16, 0x0020_u16, 0xd801_u16, 0xdc88_u16, + 0xd801_u16, 0xdc9a_u16, 0xd801_u16, 0xdc8d_u16, + 0x0020_u16, 0xd801_u16, 0xdc8f_u16, 0xd801_u16, + 0xdc9c_u16, 0xd801_u16, 0xdc92_u16, 0xd801_u16, + 0xdc96_u16, 0xd801_u16, 0xdc86_u16, 0x0020_u16, + 0xd801_u16, 0xdc95_u16, 0xd801_u16, 0xdc86_u16, + 0x000a_u16 ]) ]; + + for pairs.each |p| { + let (s, u) = copy *p; + assert!(to_utf16(s) == u); + assert!(from_utf16(u) == s); + assert!(from_utf16(to_utf16(s)) == s); + assert!(to_utf16(from_utf16(u)) == u); + } + } + + #[test] + fn test_char_at() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = 0; + for v.each |ch| { + assert!(s.char_at(pos) == *ch); + pos += from_char(*ch).len(); + } + } + + #[test] + fn test_char_at_reverse() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = s.len(); + for v.each_reverse |ch| { + assert!(s.char_at_reverse(pos) == *ch); + pos -= from_char(*ch).len(); + } + } + + #[test] + fn test_each() { + let s = ~"ศไทย中华Việt Nam"; + let v = [ + 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, + 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, + 109 + ]; + let mut pos = 0; + + for s.each |b| { + assert_eq!(b, v[pos]); + pos += 1; + } + } + + #[test] + fn test_each_empty() { + for "".each |b| { + assert_eq!(b, 0u8); + } + } + + #[test] + fn test_eachi() { + let s = ~"ศไทย中华Việt Nam"; + let v = [ + 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, + 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, + 109 + ]; + let mut pos = 0; + + for s.eachi |i, b| { + assert_eq!(pos, i); + assert_eq!(b, v[pos]); + pos += 1; + } + } + + #[test] + fn test_eachi_empty() { + for "".eachi |i, b| { + assert_eq!(i, 0); + assert_eq!(b, 0); + } + } + + #[test] + fn test_each_reverse() { + let s = ~"ศไทย中华Việt Nam"; + let v = [ + 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, + 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, + 109 + ]; + let mut pos = v.len(); + + for s.each_reverse |b| { + pos -= 1; + assert_eq!(b, v[pos]); + } + } + + #[test] + fn test_each_empty_reverse() { + for "".each_reverse |b| { + assert_eq!(b, 0u8); + } + } + + #[test] + fn test_eachi_reverse() { + let s = ~"ศไทย中华Việt Nam"; + let v = [ + 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, + 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, + 109 + ]; + let mut pos = v.len(); + + for s.eachi_reverse |i, b| { + pos -= 1; + assert_eq!(pos, i); + assert_eq!(b, v[pos]); + } + } + + #[test] + fn test_eachi_reverse_empty() { + for "".eachi_reverse |i, b| { + assert_eq!(i, 0); + assert_eq!(b, 0); + } + } + + #[test] + fn test_each_char() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = 0; + for s.each_char |ch| { + assert_eq!(ch, v[pos]); + pos += 1; + } + } + + #[test] + fn test_each_chari() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = 0; + for s.each_chari |i, ch| { + assert_eq!(pos, i); + assert_eq!(ch, v[pos]); + pos += 1; + } + } + + #[test] + fn test_each_char_reverse() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = v.len(); + for s.each_char_reverse |ch| { + pos -= 1; + assert_eq!(ch, v[pos]); + } + } + + #[test] + fn test_each_chari_reverse() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + let mut pos = v.len(); + for s.each_chari_reverse |i, ch| { + pos -= 1; + assert_eq!(pos, i); + assert_eq!(ch, v[pos]); + } + } + + #[test] + fn test_escape_unicode() { + assert_eq!(escape_unicode(~"abc"), ~"\\x61\\x62\\x63"); + assert_eq!(escape_unicode(~"a c"), ~"\\x61\\x20\\x63"); + assert_eq!(escape_unicode(~"\r\n\t"), ~"\\x0d\\x0a\\x09"); + assert_eq!(escape_unicode(~"'\"\\"), ~"\\x27\\x22\\x5c"); + assert!(escape_unicode(~"\x00\x01\xfe\xff") == + ~"\\x00\\x01\\xfe\\xff"); + assert_eq!(escape_unicode(~"\u0100\uffff"), ~"\\u0100\\uffff"); + assert!(escape_unicode(~"\U00010000\U0010ffff") == + ~"\\U00010000\\U0010ffff"); + assert_eq!(escape_unicode(~"ab\ufb00"), ~"\\x61\\x62\\ufb00"); + assert_eq!(escape_unicode(~"\U0001d4ea\r"), ~"\\U0001d4ea\\x0d"); + } + + #[test] + fn test_escape_default() { + assert_eq!(escape_default(~"abc"), ~"abc"); + assert_eq!(escape_default(~"a c"), ~"a c"); + assert_eq!(escape_default(~"\r\n\t"), ~"\\r\\n\\t"); + assert_eq!(escape_default(~"'\"\\"), ~"\\'\\\"\\\\"); + assert_eq!(escape_default(~"\u0100\uffff"), ~"\\u0100\\uffff"); + assert!(escape_default(~"\U00010000\U0010ffff") == + ~"\\U00010000\\U0010ffff"); + assert_eq!(escape_default(~"ab\ufb00"), ~"ab\\ufb00"); + assert_eq!(escape_default(~"\U0001d4ea\r"), ~"\\U0001d4ea\\r"); + } + + #[test] + fn test_to_managed() { + assert_eq!((~"abc").to_managed(), @"abc"); + assert_eq!(slice("abcdef", 1, 5).to_managed(), @"bcde"); + } + + #[test] + fn test_total_ord() { + "1234".cmp(& &"123") == Greater; + "123".cmp(& &"1234") == Less; + "1234".cmp(& &"1234") == Equal; + "12345555".cmp(& &"123456") == Less; + "22".cmp(& &"1234") == Greater; + } + + #[test] + fn test_char_range_at_reverse_underflow() { + assert_eq!(char_range_at_reverse("abc", 0).next, 0); + } + + #[test] + fn test_iterator() { + use iterator::*; + let s = ~"ศไทย中华Việt Nam"; + let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m']; + + let mut pos = 0; + let mut it = s.char_iter(); + + for it.advance |c| { + assert_eq!(c, v[pos]); + pos += 1; + } + assert_eq!(pos, v.len()); + } +} diff --git a/src/libstd/str/ascii.rs b/src/libstd/str/ascii.rs new file mode 100644 index 00000000000..4e239c4c82c --- /dev/null +++ b/src/libstd/str/ascii.rs @@ -0,0 +1,287 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations on ASCII strings and characters. + +use to_str::{ToStr,ToStrConsume}; +use str; +use str::StrSlice; +use cast; +use old_iter::BaseIter; +use vec::{CopyableVector, ImmutableVector, OwnedVector}; + +/// Datatype to hold one ascii character. It is 8 bit long. +#[deriving(Clone, Eq)] +pub struct Ascii { priv chr: u8 } + +pub impl Ascii { + /// Converts a ascii character into a `u8`. + #[inline(always)] + fn to_byte(self) -> u8 { + self.chr + } + + /// Converts a ascii character into a `char`. + #[inline(always)] + fn to_char(self) -> char { + self.chr as char + } + + /// Convert to lowercase. + #[inline(always)] + fn to_lower(self) -> Ascii { + if self.chr >= 65 && self.chr <= 90 { + Ascii{chr: self.chr | 0x20 } + } else { + self + } + } + + /// Convert to uppercase. + #[inline(always)] + fn to_upper(self) -> Ascii { + if self.chr >= 97 && self.chr <= 122 { + Ascii{chr: self.chr & !0x20 } + } else { + self + } + } + + // Compares two ascii characters of equality, ignoring case. + #[inline(always)] + fn eq_ignore_case(self, other: Ascii) -> bool { + self.to_lower().chr == other.to_lower().chr + } +} + +impl ToStr for Ascii { + #[inline(always)] + fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) } +} + +/// Trait for converting into an ascii type. +pub trait AsciiCast<T> { + /// Convert to an ascii type + fn to_ascii(&self) -> T; + + /// Check if convertible to ascii + fn is_ascii(&self) -> bool; +} + +impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] { + #[inline(always)] + fn to_ascii(&self) -> &'self[Ascii] { + assert!(self.is_ascii()); + unsafe{ cast::transmute(*self) } + } + + #[inline(always)] + fn is_ascii(&self) -> bool { + for self.each |b| { + if !b.is_ascii() { return false; } + } + true + } +} + +impl<'self> AsciiCast<&'self[Ascii]> for &'self str { + #[inline(always)] + fn to_ascii(&self) -> &'self[Ascii] { + assert!(self.is_ascii()); + let (p,len): (*u8, uint) = unsafe{ cast::transmute(*self) }; + unsafe{ cast::transmute((p, len - 1))} + } + + #[inline(always)] + fn is_ascii(&self) -> bool { + for self.each |b| { + if !b.is_ascii() { return false; } + } + true + } +} + +impl AsciiCast<Ascii> for u8 { + #[inline(always)] + fn to_ascii(&self) -> Ascii { + assert!(self.is_ascii()); + Ascii{ chr: *self } + } + + #[inline(always)] + fn is_ascii(&self) -> bool { + *self & 128 == 0u8 + } +} + +impl AsciiCast<Ascii> for char { + #[inline(always)] + fn to_ascii(&self) -> Ascii { + assert!(self.is_ascii()); + Ascii{ chr: *self as u8 } + } + + #[inline(always)] + fn is_ascii(&self) -> bool { + *self - ('\x7F' & *self) == '\x00' + } +} + +/// Trait for copyless casting to an ascii vector. +pub trait OwnedAsciiCast { + /// Take ownership and cast to an ascii vector without trailing zero element. + fn to_ascii_consume(self) -> ~[Ascii]; +} + +impl OwnedAsciiCast for ~[u8] { + #[inline(always)] + fn to_ascii_consume(self) -> ~[Ascii] { + assert!(self.is_ascii()); + unsafe {cast::transmute(self)} + } +} + +impl OwnedAsciiCast for ~str { + #[inline(always)] + fn to_ascii_consume(self) -> ~[Ascii] { + assert!(self.is_ascii()); + let mut s = self; + unsafe { + str::raw::pop_byte(&mut s); + cast::transmute(s) + } + } +} + +/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str` +pub trait AsciiStr { + /// Convert to a string. + fn to_str_ascii(&self) -> ~str; + + /// Convert to vector representing a lower cased ascii string. + fn to_lower(&self) -> ~[Ascii]; + + /// Convert to vector representing a upper cased ascii string. + fn to_upper(&self) -> ~[Ascii]; + +} + +impl<'self> AsciiStr for &'self [Ascii] { + #[inline(always)] + fn to_str_ascii(&self) -> ~str { + let mut cpy = self.to_owned(); + cpy.push(0u8.to_ascii()); + unsafe {cast::transmute(cpy)} + } + + #[inline(always)] + fn to_lower(&self) -> ~[Ascii] { + self.map(|a| a.to_lower()) + } + + #[inline(always)] + fn to_upper(&self) -> ~[Ascii] { + self.map(|a| a.to_upper()) + } +} + +impl ToStrConsume for ~[Ascii] { + #[inline(always)] + fn to_str_consume(self) -> ~str { + let mut cpy = self; + cpy.push(0u8.to_ascii()); + unsafe {cast::transmute(cpy)} + } +} + +#[cfg(test)] +mod tests { + use super::*; + use str; + + macro_rules! v2ascii ( + ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]); + (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]); + ) + + #[test] + fn test_ascii() { + assert_eq!(65u8.to_ascii().to_byte(), 65u8); + assert_eq!(65u8.to_ascii().to_char(), 'A'); + assert_eq!('A'.to_ascii().to_char(), 'A'); + assert_eq!('A'.to_ascii().to_byte(), 65u8); + + assert_eq!('A'.to_ascii().to_lower().to_char(), 'a'); + assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z'); + assert_eq!('a'.to_ascii().to_upper().to_char(), 'A'); + assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z'); + + assert_eq!('@'.to_ascii().to_lower().to_char(), '@'); + assert_eq!('['.to_ascii().to_lower().to_char(), '['); + assert_eq!('`'.to_ascii().to_upper().to_char(), '`'); + assert_eq!('{'.to_ascii().to_upper().to_char(), '{'); + + assert!(str::all(~"banana", |c| c.is_ascii())); + assert!(! str::all(~"ประเทศไทย中华Việt Nam", |c| c.is_ascii())); + } + + #[test] + fn test_ascii_vec() { + assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59])); + assert_eq!("( ;".to_ascii(), v2ascii!([40, 32, 59])); + // FIXME: #5475 borrowchk error, owned vectors do not live long enough + // if chained-from directly + let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59])); + let v = ~"( ;"; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59])); + + assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#"); + assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#"); + + assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~""); + assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca"); + assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;"); + + assert!("".is_ascii()); + assert!("a".is_ascii()); + assert!(!"\u2009".is_ascii()); + + } + + #[test] + fn test_owned_ascii_vec() { + // FIXME: #4318 Compiler crashes on moving self + //assert_eq!(~"( ;".to_ascii_consume(), v2ascii!(~[40, 32, 59])); + //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume(), v2ascii!(~[40, 32, 59])); + //assert_eq!(~"( ;".to_ascii_consume_with_null(), v2ascii!(~[40, 32, 59, 0])); + //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume_with_null(), + // v2ascii!(~[40, 32, 59, 0])); + } + + #[test] + fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); } + + #[test] + fn test_ascii_to_str_consume() { + // FIXME: #4318 Compiler crashes on moving self + //assert_eq!(v2ascii!(~[40, 32, 59]).to_str_consume(), ~"( ;"); + } + + #[test] #[should_fail] + fn test_ascii_vec_fail_u8_slice() { (&[127u8, 128u8, 255u8]).to_ascii(); } + + #[test] #[should_fail] + fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); } + + #[test] #[should_fail] + fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); } + + #[test] #[should_fail] + fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); } +} diff --git a/src/libstd/sync.rs b/src/libstd/sync.rs deleted file mode 100644 index 002c6b2eca3..00000000000 --- a/src/libstd/sync.rs +++ /dev/null @@ -1,1351 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/** - * The concurrency primitives you know and love. - * - * Maybe once we have a "core exports x only to std" mechanism, these can be - * in std. - */ - -use core::unstable::sync::{Exclusive, exclusive}; -use core::ptr; -use core::task; -use core::util; - -/**************************************************************************** - * Internals - ****************************************************************************/ - -// Each waiting task receives on one of these. -#[doc(hidden)] -type WaitEnd = comm::PortOne<()>; -#[doc(hidden)] -type SignalEnd = comm::ChanOne<()>; -// A doubly-ended queue of waiting tasks. -#[doc(hidden)] -struct Waitqueue { head: comm::Port<SignalEnd>, - tail: comm::Chan<SignalEnd> } - -fn new_waitqueue() -> Waitqueue { - let (block_head, block_tail) = comm::stream(); - Waitqueue { head: block_head, tail: block_tail } -} - -// Signals one live task from the queue. -#[doc(hidden)] -fn signal_waitqueue(q: &Waitqueue) -> bool { - // The peek is mandatory to make sure recv doesn't block. - if q.head.peek() { - // Pop and send a wakeup signal. If the waiter was killed, its port - // will have closed. Keep trying until we get a live task. - if comm::try_send_one(q.head.recv(), ()) { - true - } else { - signal_waitqueue(q) - } - } else { - false - } -} - -#[doc(hidden)] -fn broadcast_waitqueue(q: &Waitqueue) -> uint { - let mut count = 0; - while q.head.peek() { - if comm::try_send_one(q.head.recv(), ()) { - count += 1; - } - } - count -} - -// The building-block used to make semaphores, mutexes, and rwlocks. -#[doc(hidden)] -struct SemInner<Q> { - count: int, - waiters: Waitqueue, - // Can be either unit or another waitqueue. Some sems shouldn't come with - // a condition variable attached, others should. - blocked: Q -} - -#[doc(hidden)] -struct Sem<Q>(Exclusive<SemInner<Q>>); - -#[doc(hidden)] -fn new_sem<Q:Owned>(count: int, q: Q) -> Sem<Q> { - Sem(exclusive(SemInner { - count: count, waiters: new_waitqueue(), blocked: q })) -} -#[doc(hidden)] -fn new_sem_and_signal(count: int, num_condvars: uint) - -> Sem<~[Waitqueue]> { - let mut queues = ~[]; - for num_condvars.times { - queues.push(new_waitqueue()); - } - new_sem(count, queues) -} - -#[doc(hidden)] -pub impl<Q:Owned> Sem<Q> { - fn acquire(&self) { - let mut waiter_nobe = None; - do (**self).with |state| { - state.count -= 1; - if state.count < 0 { - // Create waiter nobe. - let (WaitEnd, SignalEnd) = comm::oneshot(); - // Tell outer scope we need to block. - waiter_nobe = Some(WaitEnd); - // Enqueue ourself. - state.waiters.tail.send(SignalEnd); - } - } - // Uncomment if you wish to test for sem races. Not valgrind-friendly. - /* for 1000.times { task::yield(); } */ - // Need to wait outside the exclusive. - if waiter_nobe.is_some() { - let _ = comm::recv_one(waiter_nobe.unwrap()); - } - } - fn release(&self) { - do (**self).with |state| { - state.count += 1; - if state.count <= 0 { - signal_waitqueue(&state.waiters); - } - } - } -} -// FIXME(#3154) move both copies of this into Sem<Q>, and unify the 2 structs -#[doc(hidden)] -pub impl Sem<()> { - fn access<U>(&self, blk: &fn() -> U) -> U { - let mut release = None; - unsafe { - do task::unkillable { - self.acquire(); - release = Some(SemRelease(self)); - } - } - blk() - } -} -#[doc(hidden)] -pub impl Sem<~[Waitqueue]> { - fn access<U>(&self, blk: &fn() -> U) -> U { - let mut release = None; - unsafe { - do task::unkillable { - self.acquire(); - release = Some(SemAndSignalRelease(self)); - } - } - blk() - } -} - -// FIXME(#3588) should go inside of access() -#[doc(hidden)] -type SemRelease<'self> = SemReleaseGeneric<'self, ()>; -type SemAndSignalRelease<'self> = SemReleaseGeneric<'self, ~[Waitqueue]>; -struct SemReleaseGeneric<'self, Q> { sem: &'self Sem<Q> } - -#[unsafe_destructor] -impl<'self, Q:Owned> Drop for SemReleaseGeneric<'self, Q> { - fn finalize(&self) { - self.sem.release(); - } -} - -fn SemRelease<'r>(sem: &'r Sem<()>) -> SemRelease<'r> { - SemReleaseGeneric { - sem: sem - } -} - -fn SemAndSignalRelease<'r>(sem: &'r Sem<~[Waitqueue]>) - -> SemAndSignalRelease<'r> { - SemReleaseGeneric { - sem: sem - } -} - -/// A mechanism for atomic-unlock-and-deschedule blocking and signalling. -pub struct Condvar<'self> { priv sem: &'self Sem<~[Waitqueue]> } - -#[unsafe_destructor] -impl<'self> Drop for Condvar<'self> { fn finalize(&self) {} } - -pub impl<'self> Condvar<'self> { - /** - * Atomically drop the associated lock, and block until a signal is sent. - * - * # Failure - * A task which is killed (i.e., by linked failure with another task) - * while waiting on a condition variable will wake up, fail, and unlock - * the associated lock as it unwinds. - */ - fn wait(&self) { self.wait_on(0) } - - /** - * As wait(), but can specify which of multiple condition variables to - * wait on. Only a signal_on() or broadcast_on() with the same condvar_id - * will wake this thread. - * - * The associated lock must have been initialised with an appropriate - * number of condvars. The condvar_id must be between 0 and num_condvars-1 - * or else this call will fail. - * - * wait() is equivalent to wait_on(0). - */ - fn wait_on(&self, condvar_id: uint) { - // Create waiter nobe. - let (WaitEnd, SignalEnd) = comm::oneshot(); - let mut WaitEnd = Some(WaitEnd); - let mut SignalEnd = Some(SignalEnd); - let mut reacquire = None; - let mut out_of_bounds = None; - unsafe { - do task::unkillable { - // Release lock, 'atomically' enqueuing ourselves in so doing. - do (**self.sem).with |state| { - if condvar_id < state.blocked.len() { - // Drop the lock. - state.count += 1; - if state.count <= 0 { - signal_waitqueue(&state.waiters); - } - // Enqueue ourself to be woken up by a signaller. - let SignalEnd = SignalEnd.swap_unwrap(); - state.blocked[condvar_id].tail.send(SignalEnd); - } else { - out_of_bounds = Some(state.blocked.len()); - } - } - - // If yield checks start getting inserted anywhere, we can be - // killed before or after enqueueing. Deciding whether to - // unkillably reacquire the lock needs to happen atomically - // wrt enqueuing. - if out_of_bounds.is_none() { - reacquire = Some(SemAndSignalReacquire(self.sem)); - } - } - } - do check_cvar_bounds(out_of_bounds, condvar_id, "cond.wait_on()") { - // Unconditionally "block". (Might not actually block if a - // signaller already sent -- I mean 'unconditionally' in contrast - // with acquire().) - let _ = comm::recv_one(WaitEnd.swap_unwrap()); - } - - // This is needed for a failing condition variable to reacquire the - // mutex during unwinding. As long as the wrapper (mutex, etc) is - // bounded in when it gets released, this shouldn't hang forever. - struct SemAndSignalReacquire<'self> { - sem: &'self Sem<~[Waitqueue]>, - } - - #[unsafe_destructor] - impl<'self> Drop for SemAndSignalReacquire<'self> { - fn finalize(&self) { - unsafe { - // Needs to succeed, instead of itself dying. - do task::unkillable { - self.sem.acquire(); - } - } - } - } - - fn SemAndSignalReacquire<'r>(sem: &'r Sem<~[Waitqueue]>) - -> SemAndSignalReacquire<'r> { - SemAndSignalReacquire { - sem: sem - } - } - } - - /// Wake up a blocked task. Returns false if there was no blocked task. - fn signal(&self) -> bool { self.signal_on(0) } - - /// As signal, but with a specified condvar_id. See wait_on. - fn signal_on(&self, condvar_id: uint) -> bool { - let mut out_of_bounds = None; - let mut result = false; - do (**self.sem).with |state| { - if condvar_id < state.blocked.len() { - result = signal_waitqueue(&state.blocked[condvar_id]); - } else { - out_of_bounds = Some(state.blocked.len()); - } - } - do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") { - result - } - } - - /// Wake up all blocked tasks. Returns the number of tasks woken. - fn broadcast(&self) -> uint { self.broadcast_on(0) } - - /// As broadcast, but with a specified condvar_id. See wait_on. - fn broadcast_on(&self, condvar_id: uint) -> uint { - let mut out_of_bounds = None; - let mut queue = None; - do (**self.sem).with |state| { - if condvar_id < state.blocked.len() { - // To avoid :broadcast_heavy, we make a new waitqueue, - // swap it out with the old one, and broadcast on the - // old one outside of the little-lock. - queue = Some(util::replace(&mut state.blocked[condvar_id], - new_waitqueue())); - } else { - out_of_bounds = Some(state.blocked.len()); - } - } - do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") { - let queue = queue.swap_unwrap(); - broadcast_waitqueue(&queue) - } - } -} - -// Checks whether a condvar ID was out of bounds, and fails if so, or does -// something else next on success. -#[inline(always)] -#[doc(hidden)] -fn check_cvar_bounds<U>(out_of_bounds: Option<uint>, id: uint, act: &str, - blk: &fn() -> U) -> U { - match out_of_bounds { - Some(0) => - fail!("%s with illegal ID %u - this lock has no condvars!", act, id), - Some(length) => - fail!("%s with illegal ID %u - ID must be less than %u", act, id, length), - None => blk() - } -} - -#[doc(hidden)] -pub impl Sem<~[Waitqueue]> { - // The only other place that condvars get built is rwlock_write_mode. - fn access_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U { - do self.access { blk(&Condvar { sem: self }) } - } -} - -/**************************************************************************** - * Semaphores - ****************************************************************************/ - -/// A counting, blocking, bounded-waiting semaphore. -struct Semaphore { priv sem: Sem<()> } - -/// Create a new semaphore with the specified count. -pub fn semaphore(count: int) -> Semaphore { - Semaphore { sem: new_sem(count, ()) } -} - -impl Clone for Semaphore { - /// Create a new handle to the semaphore. - fn clone(&self) -> Semaphore { - Semaphore { sem: Sem((*self.sem).clone()) } - } -} - -pub impl Semaphore { - /** - * Acquire a resource represented by the semaphore. Blocks if necessary - * until resource(s) become available. - */ - fn acquire(&self) { (&self.sem).acquire() } - - /** - * Release a held resource represented by the semaphore. Wakes a blocked - * contending task, if any exist. Won't block the caller. - */ - fn release(&self) { (&self.sem).release() } - - /// Run a function with ownership of one of the semaphore's resources. - fn access<U>(&self, blk: &fn() -> U) -> U { (&self.sem).access(blk) } -} - -/**************************************************************************** - * Mutexes - ****************************************************************************/ - -/** - * A blocking, bounded-waiting, mutual exclusion lock with an associated - * FIFO condition variable. - * - * # Failure - * A task which fails while holding a mutex will unlock the mutex as it - * unwinds. - */ -pub struct Mutex { priv sem: Sem<~[Waitqueue]> } - -/// Create a new mutex, with one associated condvar. -pub fn Mutex() -> Mutex { mutex_with_condvars(1) } -/** - * Create a new mutex, with a specified number of associated condvars. This - * will allow calling wait_on/signal_on/broadcast_on with condvar IDs between - * 0 and num_condvars-1. (If num_condvars is 0, lock_cond will be allowed but - * any operations on the condvar will fail.) - */ -pub fn mutex_with_condvars(num_condvars: uint) -> Mutex { - Mutex { sem: new_sem_and_signal(1, num_condvars) } -} - -impl Clone for Mutex { - /// Create a new handle to the mutex. - fn clone(&self) -> Mutex { Mutex { sem: Sem((*self.sem).clone()) } } -} - -pub impl Mutex { - /// Run a function with ownership of the mutex. - fn lock<U>(&self, blk: &fn() -> U) -> U { (&self.sem).access(blk) } - - /// Run a function with ownership of the mutex and a handle to a condvar. - fn lock_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U { - (&self.sem).access_cond(blk) - } -} - -/**************************************************************************** - * Reader-writer locks - ****************************************************************************/ - -// NB: Wikipedia - Readers-writers_problem#The_third_readers-writers_problem - -#[doc(hidden)] -struct RWlockInner { - read_mode: bool, - read_count: uint -} - -/** - * A blocking, no-starvation, reader-writer lock with an associated condvar. - * - * # Failure - * A task which fails while holding an rwlock will unlock the rwlock as it - * unwinds. - */ -pub struct RWlock { - priv order_lock: Semaphore, - priv access_lock: Sem<~[Waitqueue]>, - priv state: Exclusive<RWlockInner> -} - -/// Create a new rwlock, with one associated condvar. -pub fn RWlock() -> RWlock { rwlock_with_condvars(1) } - -/** - * Create a new rwlock, with a specified number of associated condvars. - * Similar to mutex_with_condvars. - */ -pub fn rwlock_with_condvars(num_condvars: uint) -> RWlock { - RWlock { order_lock: semaphore(1), - access_lock: new_sem_and_signal(1, num_condvars), - state: exclusive(RWlockInner { read_mode: false, - read_count: 0 }) } -} - -pub impl RWlock { - /// Create a new handle to the rwlock. - fn clone(&self) -> RWlock { - RWlock { order_lock: (&(self.order_lock)).clone(), - access_lock: Sem((*self.access_lock).clone()), - state: self.state.clone() } - } - - /** - * Run a function with the rwlock in read mode. Calls to 'read' from other - * tasks may run concurrently with this one. - */ - fn read<U>(&self, blk: &fn() -> U) -> U { - let mut release = None; - unsafe { - do task::unkillable { - do (&self.order_lock).access { - let mut first_reader = false; - do self.state.with |state| { - first_reader = (state.read_count == 0); - state.read_count += 1; - } - if first_reader { - (&self.access_lock).acquire(); - do self.state.with |state| { - // Must happen *after* getting access_lock. If - // this is set while readers are waiting, but - // while a writer holds the lock, the writer will - // be confused if they downgrade-then-unlock. - state.read_mode = true; - } - } - } - release = Some(RWlockReleaseRead(self)); - } - } - blk() - } - - /** - * Run a function with the rwlock in write mode. No calls to 'read' or - * 'write' from other tasks will run concurrently with this one. - */ - fn write<U>(&self, blk: &fn() -> U) -> U { - unsafe { - do task::unkillable { - (&self.order_lock).acquire(); - do (&self.access_lock).access { - (&self.order_lock).release(); - task::rekillable(blk) - } - } - } - } - - /** - * As write(), but also with a handle to a condvar. Waiting on this - * condvar will allow readers and writers alike to take the rwlock before - * the waiting task is signalled. (Note: a writer that waited and then - * was signalled might reacquire the lock before other waiting writers.) - */ - fn write_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U { - // NB: You might think I should thread the order_lock into the cond - // wait call, so that it gets waited on before access_lock gets - // reacquired upon being woken up. However, (a) this would be not - // pleasant to implement (and would mandate a new 'rw_cond' type) and - // (b) I think violating no-starvation in that case is appropriate. - unsafe { - do task::unkillable { - (&self.order_lock).acquire(); - do (&self.access_lock).access_cond |cond| { - (&self.order_lock).release(); - do task::rekillable { blk(cond) } - } - } - } - } - - /** - * As write(), but with the ability to atomically 'downgrade' the lock; - * i.e., to become a reader without letting other writers get the lock in - * the meantime (such as unlocking and then re-locking as a reader would - * do). The block takes a "write mode token" argument, which can be - * transformed into a "read mode token" by calling downgrade(). Example: - * ~~~ - * do lock.write_downgrade |write_mode| { - * do (&write_mode).write_cond |condvar| { - * ... exclusive access ... - * } - * let read_mode = lock.downgrade(write_mode); - * do (&read_mode).read { - * ... shared access ... - * } - * } - * ~~~ - */ - fn write_downgrade<U>(&self, blk: &fn(v: RWlockWriteMode) -> U) -> U { - // Implementation slightly different from the slicker 'write's above. - // The exit path is conditional on whether the caller downgrades. - let mut _release = None; - unsafe { - do task::unkillable { - (&self.order_lock).acquire(); - (&self.access_lock).acquire(); - (&self.order_lock).release(); - } - _release = Some(RWlockReleaseDowngrade(self)); - } - blk(RWlockWriteMode { lock: self }) - } - - /// To be called inside of the write_downgrade block. - fn downgrade<'a>(&self, - token: RWlockWriteMode<'a>) - -> RWlockReadMode<'a> { - if !ptr::ref_eq(self, token.lock) { - fail!("Can't downgrade() with a different rwlock's write_mode!"); - } - unsafe { - do task::unkillable { - let mut first_reader = false; - do self.state.with |state| { - assert!(!state.read_mode); - state.read_mode = true; - first_reader = (state.read_count == 0); - state.read_count += 1; - } - if !first_reader { - // Guaranteed not to let another writer in, because - // another reader was holding the order_lock. Hence they - // must be the one to get the access_lock (because all - // access_locks are acquired with order_lock held). - (&self.access_lock).release(); - } - } - } - RWlockReadMode { lock: token.lock } - } -} - -// FIXME(#3588) should go inside of read() -#[doc(hidden)] -struct RWlockReleaseRead<'self> { - lock: &'self RWlock, -} - -#[unsafe_destructor] -impl<'self> Drop for RWlockReleaseRead<'self> { - fn finalize(&self) { - unsafe { - do task::unkillable { - let mut last_reader = false; - do self.lock.state.with |state| { - assert!(state.read_mode); - assert!(state.read_count > 0); - state.read_count -= 1; - if state.read_count == 0 { - last_reader = true; - state.read_mode = false; - } - } - if last_reader { - (&self.lock.access_lock).release(); - } - } - } - } -} - -fn RWlockReleaseRead<'r>(lock: &'r RWlock) -> RWlockReleaseRead<'r> { - RWlockReleaseRead { - lock: lock - } -} - -// FIXME(#3588) should go inside of downgrade() -#[doc(hidden)] -#[unsafe_destructor] -struct RWlockReleaseDowngrade<'self> { - lock: &'self RWlock, -} - -#[unsafe_destructor] -impl<'self> Drop for RWlockReleaseDowngrade<'self> { - fn finalize(&self) { - unsafe { - do task::unkillable { - let mut writer_or_last_reader = false; - do self.lock.state.with |state| { - if state.read_mode { - assert!(state.read_count > 0); - state.read_count -= 1; - if state.read_count == 0 { - // Case 1: Writer downgraded & was the last reader - writer_or_last_reader = true; - state.read_mode = false; - } else { - // Case 2: Writer downgraded & was not the last - // reader - } - } else { - // Case 3: Writer did not downgrade - writer_or_last_reader = true; - } - } - if writer_or_last_reader { - (&self.lock.access_lock).release(); - } - } - } - } -} - -fn RWlockReleaseDowngrade<'r>(lock: &'r RWlock) - -> RWlockReleaseDowngrade<'r> { - RWlockReleaseDowngrade { - lock: lock - } -} - -/// The "write permission" token used for rwlock.write_downgrade(). -pub struct RWlockWriteMode<'self> { priv lock: &'self RWlock } -#[unsafe_destructor] -impl<'self> Drop for RWlockWriteMode<'self> { fn finalize(&self) {} } - -/// The "read permission" token used for rwlock.write_downgrade(). -pub struct RWlockReadMode<'self> { priv lock: &'self RWlock } -#[unsafe_destructor] -impl<'self> Drop for RWlockReadMode<'self> { fn finalize(&self) {} } - -pub impl<'self> RWlockWriteMode<'self> { - /// Access the pre-downgrade rwlock in write mode. - fn write<U>(&self, blk: &fn() -> U) -> U { blk() } - /// Access the pre-downgrade rwlock in write mode with a condvar. - fn write_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U { - blk(&Condvar { sem: &self.lock.access_lock }) - } -} - -pub impl<'self> RWlockReadMode<'self> { - /// Access the post-downgrade rwlock in read mode. - fn read<U>(&self, blk: &fn() -> U) -> U { blk() } -} - -/**************************************************************************** - * Tests - ****************************************************************************/ - -#[cfg(test)] -mod tests { - - use sync::*; - - use core::cast; - use core::cell::Cell; - use core::ptr; - use core::result; - use core::task; - use core::vec; - - /************************************************************************ - * Semaphore tests - ************************************************************************/ - #[test] - fn test_sem_acquire_release() { - let s = ~semaphore(1); - s.acquire(); - s.release(); - s.acquire(); - } - #[test] - fn test_sem_basic() { - let s = ~semaphore(1); - do s.access { } - } - #[test] - fn test_sem_as_mutex() { - let s = ~semaphore(1); - let s2 = ~s.clone(); - do task::spawn || { - do s2.access { - for 5.times { task::yield(); } - } - } - do s.access { - for 5.times { task::yield(); } - } - } - #[test] - fn test_sem_as_cvar() { - /* Child waits and parent signals */ - let (p,c) = comm::stream(); - let s = ~semaphore(0); - let s2 = ~s.clone(); - do task::spawn || { - s2.acquire(); - c.send(()); - } - for 5.times { task::yield(); } - s.release(); - let _ = p.recv(); - - /* Parent waits and child signals */ - let (p,c) = comm::stream(); - let s = ~semaphore(0); - let s2 = ~s.clone(); - do task::spawn || { - for 5.times { task::yield(); } - s2.release(); - let _ = p.recv(); - } - s.acquire(); - c.send(()); - } - #[test] - fn test_sem_multi_resource() { - // Parent and child both get in the critical section at the same - // time, and shake hands. - let s = ~semaphore(2); - let s2 = ~s.clone(); - let (p1,c1) = comm::stream(); - let (p2,c2) = comm::stream(); - do task::spawn || { - do s2.access { - let _ = p2.recv(); - c1.send(()); - } - } - do s.access { - c2.send(()); - let _ = p1.recv(); - } - } - #[test] - fn test_sem_runtime_friendly_blocking() { - // Force the runtime to schedule two threads on the same sched_loop. - // When one blocks, it should schedule the other one. - do task::spawn_sched(task::ManualThreads(1)) { - let s = ~semaphore(1); - let s2 = ~s.clone(); - let (p,c) = comm::stream(); - let child_data = Cell((s2, c)); - do s.access { - let (s2, c) = child_data.take(); - do task::spawn || { - c.send(()); - do s2.access { } - c.send(()); - } - let _ = p.recv(); // wait for child to come alive - for 5.times { task::yield(); } // let the child contend - } - let _ = p.recv(); // wait for child to be done - } - } - /************************************************************************ - * Mutex tests - ************************************************************************/ - #[test] - fn test_mutex_lock() { - // Unsafely achieve shared state, and do the textbook - // "load tmp = move ptr; inc tmp; store ptr <- tmp" dance. - let (p,c) = comm::stream(); - let m = ~Mutex(); - let m2 = m.clone(); - let mut sharedstate = ~0; - { - let ptr: *int = &*sharedstate; - do task::spawn || { - let sharedstate: &mut int = - unsafe { cast::transmute(ptr) }; - access_shared(sharedstate, m2, 10); - c.send(()); - - } - } - { - access_shared(sharedstate, m, 10); - let _ = p.recv(); - - assert_eq!(*sharedstate, 20); - } - - fn access_shared(sharedstate: &mut int, m: &Mutex, n: uint) { - for n.times { - do m.lock { - let oldval = *sharedstate; - task::yield(); - *sharedstate = oldval + 1; - } - } - } - } - #[test] - fn test_mutex_cond_wait() { - let m = ~Mutex(); - - // Child wakes up parent - do m.lock_cond |cond| { - let m2 = ~m.clone(); - do task::spawn || { - do m2.lock_cond |cond| { - let woken = cond.signal(); - assert!(woken); - } - } - cond.wait(); - } - // Parent wakes up child - let (port,chan) = comm::stream(); - let m3 = ~m.clone(); - do task::spawn || { - do m3.lock_cond |cond| { - chan.send(()); - cond.wait(); - chan.send(()); - } - } - let _ = port.recv(); // Wait until child gets in the mutex - do m.lock_cond |cond| { - let woken = cond.signal(); - assert!(woken); - } - let _ = port.recv(); // Wait until child wakes up - } - #[cfg(test)] - fn test_mutex_cond_broadcast_helper(num_waiters: uint) { - let m = ~Mutex(); - let mut ports = ~[]; - - for num_waiters.times { - let mi = ~m.clone(); - let (port, chan) = comm::stream(); - ports.push(port); - do task::spawn || { - do mi.lock_cond |cond| { - chan.send(()); - cond.wait(); - chan.send(()); - } - } - } - - // wait until all children get in the mutex - for ports.each |port| { let _ = port.recv(); } - do m.lock_cond |cond| { - let num_woken = cond.broadcast(); - assert_eq!(num_woken, num_waiters); - } - // wait until all children wake up - for ports.each |port| { let _ = port.recv(); } - } - #[test] - fn test_mutex_cond_broadcast() { - test_mutex_cond_broadcast_helper(12); - } - #[test] - fn test_mutex_cond_broadcast_none() { - test_mutex_cond_broadcast_helper(0); - } - #[test] - fn test_mutex_cond_no_waiter() { - let m = ~Mutex(); - let m2 = ~m.clone(); - do task::try || { - do m.lock_cond |_x| { } - }; - do m2.lock_cond |cond| { - assert!(!cond.signal()); - } - } - #[test] #[ignore(cfg(windows))] - fn test_mutex_killed_simple() { - // Mutex must get automatically unlocked if failed/killed within. - let m = ~Mutex(); - let m2 = ~m.clone(); - - let result: result::Result<(),()> = do task::try || { - do m2.lock { - fail!(); - } - }; - assert!(result.is_err()); - // child task must have finished by the time try returns - do m.lock { } - } - #[test] #[ignore(cfg(windows))] - fn test_mutex_killed_cond() { - // Getting killed during cond wait must not corrupt the mutex while - // unwinding (e.g. double unlock). - let m = ~Mutex(); - let m2 = ~m.clone(); - - let result: result::Result<(),()> = do task::try || { - let (p,c) = comm::stream(); - do task::spawn || { // linked - let _ = p.recv(); // wait for sibling to get in the mutex - task::yield(); - fail!(); - } - do m2.lock_cond |cond| { - c.send(()); // tell sibling go ahead - cond.wait(); // block forever - } - }; - assert!(result.is_err()); - // child task must have finished by the time try returns - do m.lock_cond |cond| { - let woken = cond.signal(); - assert!(!woken); - } - } - #[test] #[ignore(cfg(windows))] - fn test_mutex_killed_broadcast() { - let m = ~Mutex(); - let m2 = ~m.clone(); - let (p,c) = comm::stream(); - - let result: result::Result<(),()> = do task::try || { - let mut sibling_convos = ~[]; - for 2.times { - let (p,c) = comm::stream(); - let c = Cell(c); - sibling_convos.push(p); - let mi = ~m2.clone(); - // spawn sibling task - do task::spawn { // linked - do mi.lock_cond |cond| { - let c = c.take(); - c.send(()); // tell sibling to go ahead - let _z = SendOnFailure(c); - cond.wait(); // block forever - } - } - } - for sibling_convos.each |p| { - let _ = p.recv(); // wait for sibling to get in the mutex - } - do m2.lock { } - c.send(sibling_convos); // let parent wait on all children - fail!(); - }; - assert!(result.is_err()); - // child task must have finished by the time try returns - for vec::each(p.recv()) |p| { p.recv(); } // wait on all its siblings - do m.lock_cond |cond| { - let woken = cond.broadcast(); - assert_eq!(woken, 0); - } - struct SendOnFailure { - c: comm::Chan<()>, - } - - impl Drop for SendOnFailure { - fn finalize(&self) { - self.c.send(()); - } - } - - fn SendOnFailure(c: comm::Chan<()>) -> SendOnFailure { - SendOnFailure { - c: c - } - } - } - #[test] - fn test_mutex_cond_signal_on_0() { - // Tests that signal_on(0) is equivalent to signal(). - let m = ~Mutex(); - do m.lock_cond |cond| { - let m2 = ~m.clone(); - do task::spawn || { - do m2.lock_cond |cond| { - cond.signal_on(0); - } - } - cond.wait(); - } - } - #[test] #[ignore(cfg(windows))] - fn test_mutex_different_conds() { - let result = do task::try { - let m = ~mutex_with_condvars(2); - let m2 = ~m.clone(); - let (p,c) = comm::stream(); - do task::spawn || { - do m2.lock_cond |cond| { - c.send(()); - cond.wait_on(1); - } - } - let _ = p.recv(); - do m.lock_cond |cond| { - if !cond.signal_on(0) { - fail!(); // success; punt sibling awake. - } - } - }; - assert!(result.is_err()); - } - #[test] #[ignore(cfg(windows))] - fn test_mutex_no_condvars() { - let result = do task::try { - let m = ~mutex_with_condvars(0); - do m.lock_cond |cond| { cond.wait(); } - }; - assert!(result.is_err()); - let result = do task::try { - let m = ~mutex_with_condvars(0); - do m.lock_cond |cond| { cond.signal(); } - }; - assert!(result.is_err()); - let result = do task::try { - let m = ~mutex_with_condvars(0); - do m.lock_cond |cond| { cond.broadcast(); } - }; - assert!(result.is_err()); - } - /************************************************************************ - * Reader/writer lock tests - ************************************************************************/ - #[cfg(test)] - pub enum RWlockMode { Read, Write, Downgrade, DowngradeRead } - #[cfg(test)] - fn lock_rwlock_in_mode(x: &RWlock, mode: RWlockMode, blk: &fn()) { - match mode { - Read => x.read(blk), - Write => x.write(blk), - Downgrade => - do x.write_downgrade |mode| { - (&mode).write(blk); - }, - DowngradeRead => - do x.write_downgrade |mode| { - let mode = x.downgrade(mode); - (&mode).read(blk); - }, - } - } - #[cfg(test)] - fn test_rwlock_exclusion(x: ~RWlock, - mode1: RWlockMode, - mode2: RWlockMode) { - // Test mutual exclusion between readers and writers. Just like the - // mutex mutual exclusion test, a ways above. - let (p,c) = comm::stream(); - let x2 = (*x).clone(); - let mut sharedstate = ~0; - { - let ptr: *int = &*sharedstate; - do task::spawn || { - let sharedstate: &mut int = - unsafe { cast::transmute(ptr) }; - access_shared(sharedstate, &x2, mode1, 10); - c.send(()); - } - } - { - access_shared(sharedstate, x, mode2, 10); - let _ = p.recv(); - - assert_eq!(*sharedstate, 20); - } - - fn access_shared(sharedstate: &mut int, x: &RWlock, mode: RWlockMode, - n: uint) { - for n.times { - do lock_rwlock_in_mode(x, mode) { - let oldval = *sharedstate; - task::yield(); - *sharedstate = oldval + 1; - } - } - } - } - #[test] - fn test_rwlock_readers_wont_modify_the_data() { - test_rwlock_exclusion(~RWlock(), Read, Write); - test_rwlock_exclusion(~RWlock(), Write, Read); - test_rwlock_exclusion(~RWlock(), Read, Downgrade); - test_rwlock_exclusion(~RWlock(), Downgrade, Read); - } - #[test] - fn test_rwlock_writers_and_writers() { - test_rwlock_exclusion(~RWlock(), Write, Write); - test_rwlock_exclusion(~RWlock(), Write, Downgrade); - test_rwlock_exclusion(~RWlock(), Downgrade, Write); - test_rwlock_exclusion(~RWlock(), Downgrade, Downgrade); - } - #[cfg(test)] - fn test_rwlock_handshake(x: ~RWlock, - mode1: RWlockMode, - mode2: RWlockMode, - make_mode2_go_first: bool) { - // Much like sem_multi_resource. - let x2 = (*x).clone(); - let (p1,c1) = comm::stream(); - let (p2,c2) = comm::stream(); - do task::spawn || { - if !make_mode2_go_first { - let _ = p2.recv(); // parent sends to us once it locks, or ... - } - do lock_rwlock_in_mode(&x2, mode2) { - if make_mode2_go_first { - c1.send(()); // ... we send to it once we lock - } - let _ = p2.recv(); - c1.send(()); - } - } - if make_mode2_go_first { - let _ = p1.recv(); // child sends to us once it locks, or ... - } - do lock_rwlock_in_mode(x, mode1) { - if !make_mode2_go_first { - c2.send(()); // ... we send to it once we lock - } - c2.send(()); - let _ = p1.recv(); - } - } - #[test] - fn test_rwlock_readers_and_readers() { - test_rwlock_handshake(~RWlock(), Read, Read, false); - // The downgrader needs to get in before the reader gets in, otherwise - // they cannot end up reading at the same time. - test_rwlock_handshake(~RWlock(), DowngradeRead, Read, false); - test_rwlock_handshake(~RWlock(), Read, DowngradeRead, true); - // Two downgrade_reads can never both end up reading at the same time. - } - #[test] - fn test_rwlock_downgrade_unlock() { - // Tests that downgrade can unlock the lock in both modes - let x = ~RWlock(); - do lock_rwlock_in_mode(x, Downgrade) { } - test_rwlock_handshake(x, Read, Read, false); - let y = ~RWlock(); - do lock_rwlock_in_mode(y, DowngradeRead) { } - test_rwlock_exclusion(y, Write, Write); - } - #[test] - fn test_rwlock_read_recursive() { - let x = ~RWlock(); - do x.read { do x.read { } } - } - #[test] - fn test_rwlock_cond_wait() { - // As test_mutex_cond_wait above. - let x = ~RWlock(); - - // Child wakes up parent - do x.write_cond |cond| { - let x2 = (*x).clone(); - do task::spawn || { - do x2.write_cond |cond| { - let woken = cond.signal(); - assert!(woken); - } - } - cond.wait(); - } - // Parent wakes up child - let (port,chan) = comm::stream(); - let x3 = (*x).clone(); - do task::spawn || { - do x3.write_cond |cond| { - chan.send(()); - cond.wait(); - chan.send(()); - } - } - let _ = port.recv(); // Wait until child gets in the rwlock - do x.read { } // Must be able to get in as a reader in the meantime - do x.write_cond |cond| { // Or as another writer - let woken = cond.signal(); - assert!(woken); - } - let _ = port.recv(); // Wait until child wakes up - do x.read { } // Just for good measure - } - #[cfg(test)] - fn test_rwlock_cond_broadcast_helper(num_waiters: uint, - dg1: bool, - dg2: bool) { - // Much like the mutex broadcast test. Downgrade-enabled. - fn lock_cond(x: &RWlock, downgrade: bool, blk: &fn(c: &Condvar)) { - if downgrade { - do x.write_downgrade |mode| { - (&mode).write_cond(blk) - } - } else { - x.write_cond(blk) - } - } - let x = ~RWlock(); - let mut ports = ~[]; - - for num_waiters.times { - let xi = (*x).clone(); - let (port, chan) = comm::stream(); - ports.push(port); - do task::spawn || { - do lock_cond(&xi, dg1) |cond| { - chan.send(()); - cond.wait(); - chan.send(()); - } - } - } - - // wait until all children get in the mutex - for ports.each |port| { let _ = port.recv(); } - do lock_cond(x, dg2) |cond| { - let num_woken = cond.broadcast(); - assert_eq!(num_woken, num_waiters); - } - // wait until all children wake up - for ports.each |port| { let _ = port.recv(); } - } - #[test] - fn test_rwlock_cond_broadcast() { - test_rwlock_cond_broadcast_helper(0, true, true); - test_rwlock_cond_broadcast_helper(0, true, false); - test_rwlock_cond_broadcast_helper(0, false, true); - test_rwlock_cond_broadcast_helper(0, false, false); - test_rwlock_cond_broadcast_helper(12, true, true); - test_rwlock_cond_broadcast_helper(12, true, false); - test_rwlock_cond_broadcast_helper(12, false, true); - test_rwlock_cond_broadcast_helper(12, false, false); - } - #[cfg(test)] #[ignore(cfg(windows))] - fn rwlock_kill_helper(mode1: RWlockMode, mode2: RWlockMode) { - // Mutex must get automatically unlocked if failed/killed within. - let x = ~RWlock(); - let x2 = (*x).clone(); - - let result: result::Result<(),()> = do task::try || { - do lock_rwlock_in_mode(&x2, mode1) { - fail!(); - } - }; - assert!(result.is_err()); - // child task must have finished by the time try returns - do lock_rwlock_in_mode(x, mode2) { } - } - #[test] #[ignore(cfg(windows))] - fn test_rwlock_reader_killed_writer() { - rwlock_kill_helper(Read, Write); - } - #[test] #[ignore(cfg(windows))] - fn test_rwlock_writer_killed_reader() { - rwlock_kill_helper(Write,Read ); - } - #[test] #[ignore(cfg(windows))] - fn test_rwlock_reader_killed_reader() { - rwlock_kill_helper(Read, Read ); - } - #[test] #[ignore(cfg(windows))] - fn test_rwlock_writer_killed_writer() { - rwlock_kill_helper(Write,Write); - } - #[test] #[ignore(cfg(windows))] - fn test_rwlock_kill_downgrader() { - rwlock_kill_helper(Downgrade, Read); - rwlock_kill_helper(Read, Downgrade); - rwlock_kill_helper(Downgrade, Write); - rwlock_kill_helper(Write, Downgrade); - rwlock_kill_helper(DowngradeRead, Read); - rwlock_kill_helper(Read, DowngradeRead); - rwlock_kill_helper(DowngradeRead, Write); - rwlock_kill_helper(Write, DowngradeRead); - rwlock_kill_helper(DowngradeRead, Downgrade); - rwlock_kill_helper(DowngradeRead, Downgrade); - rwlock_kill_helper(Downgrade, DowngradeRead); - rwlock_kill_helper(Downgrade, DowngradeRead); - } - #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_rwlock_downgrade_cant_swap() { - // Tests that you can't downgrade with a different rwlock's token. - let x = ~RWlock(); - let y = ~RWlock(); - do x.write_downgrade |xwrite| { - let mut xopt = Some(xwrite); - do y.write_downgrade |_ywrite| { - y.downgrade(xopt.swap_unwrap()); - error!("oops, y.downgrade(x) should have failed!"); - } - } - } -} diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs new file mode 100644 index 00000000000..137070ce202 --- /dev/null +++ b/src/libstd/sys.rs @@ -0,0 +1,352 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Misc low level stuff + +use option::{Some, None}; +use cast; +use cmp::{Eq, Ord}; +use gc; +use io; +use libc; +use libc::{c_void, c_char, size_t}; +use repr; +use str; +use unstable::intrinsics; + +pub type FreeGlue<'self> = &'self fn(*TypeDesc, *c_void); + +// Corresponds to runtime type_desc type +pub struct TypeDesc { + size: uint, + align: uint, + take_glue: uint, + drop_glue: uint, + free_glue: uint + // Remaining fields not listed +} + +/// The representation of a Rust closure +pub struct Closure { + code: *(), + env: *(), +} + +pub mod rustrt { + use libc::{c_char, size_t}; + + pub extern { + #[rust_stack] + unsafe fn rust_upcall_fail(expr: *c_char, + file: *c_char, + line: size_t); + } +} + +/// Compares contents of two pointers using the default method. +/// Equivalent to `*x1 == *x2`. Useful for hashtables. +pub fn shape_eq<T:Eq>(x1: &T, x2: &T) -> bool { + *x1 == *x2 +} + +pub fn shape_lt<T:Ord>(x1: &T, x2: &T) -> bool { + *x1 < *x2 +} + +pub fn shape_le<T:Ord>(x1: &T, x2: &T) -> bool { + *x1 <= *x2 +} + +/** + * Returns a pointer to a type descriptor. + * + * Useful for calling certain function in the Rust runtime or otherwise + * performing dark magick. + */ +#[inline(always)] +pub fn get_type_desc<T>() -> *TypeDesc { + unsafe { intrinsics::get_tydesc::<T>() as *TypeDesc } +} + +/// Returns a pointer to a type descriptor. +#[inline(always)] +pub fn get_type_desc_val<T>(_val: &T) -> *TypeDesc { + get_type_desc::<T>() +} + +/// Returns the size of a type +#[inline(always)] +pub fn size_of<T>() -> uint { + unsafe { intrinsics::size_of::<T>() } +} + +/// Returns the size of the type that `_val` points to +#[inline(always)] +pub fn size_of_val<T>(_val: &T) -> uint { + size_of::<T>() +} + +/** + * Returns the size of a type, or 1 if the actual size is zero. + * + * Useful for building structures containing variable-length arrays. + */ +#[inline(always)] +pub fn nonzero_size_of<T>() -> uint { + let s = size_of::<T>(); + if s == 0 { 1 } else { s } +} + +/// Returns the size of the type of the value that `_val` points to +#[inline(always)] +pub fn nonzero_size_of_val<T>(_val: &T) -> uint { + nonzero_size_of::<T>() +} + + +/** + * Returns the ABI-required minimum alignment of a type + * + * This is the alignment used for struct fields. It may be smaller + * than the preferred alignment. + */ +#[inline(always)] +pub fn min_align_of<T>() -> uint { + unsafe { intrinsics::min_align_of::<T>() } +} + +/// Returns the ABI-required minimum alignment of the type of the value that +/// `_val` points to +#[inline(always)] +pub fn min_align_of_val<T>(_val: &T) -> uint { + min_align_of::<T>() +} + +/// Returns the preferred alignment of a type +#[inline(always)] +pub fn pref_align_of<T>() -> uint { + unsafe { intrinsics::pref_align_of::<T>() } +} + +/// Returns the preferred alignment of the type of the value that +/// `_val` points to +#[inline(always)] +pub fn pref_align_of_val<T>(_val: &T) -> uint { + pref_align_of::<T>() +} + +/// Returns the refcount of a shared box (as just before calling this) +#[inline(always)] +pub fn refcount<T>(t: @T) -> uint { + unsafe { + let ref_ptr: *uint = cast::transmute_copy(&t); + *ref_ptr - 1 + } +} + +pub fn log_str<T>(t: &T) -> ~str { + do io::with_str_writer |wr| { + repr::write_repr(wr, t) + } +} + +/// Trait for initiating task failure. +pub trait FailWithCause { + /// Fail the current task, taking ownership of `cause` + fn fail_with(cause: Self, file: &'static str, line: uint) -> !; +} + +impl FailWithCause for ~str { + fn fail_with(cause: ~str, file: &'static str, line: uint) -> ! { + do str::as_buf(cause) |msg_buf, _msg_len| { + do str::as_buf(file) |file_buf, _file_len| { + unsafe { + let msg_buf = cast::transmute(msg_buf); + let file_buf = cast::transmute(file_buf); + begin_unwind_(msg_buf, file_buf, line as libc::size_t) + } + } + } + } +} + +impl FailWithCause for &'static str { + fn fail_with(cause: &'static str, file: &'static str, line: uint) -> ! { + do str::as_buf(cause) |msg_buf, _msg_len| { + do str::as_buf(file) |file_buf, _file_len| { + unsafe { + let msg_buf = cast::transmute(msg_buf); + let file_buf = cast::transmute(file_buf); + begin_unwind_(msg_buf, file_buf, line as libc::size_t) + } + } + } + } +} + +// FIXME #4427: Temporary until rt::rt_fail_ goes away +pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { + use option::Option; + use rt::{context, OldTaskContext, TaskContext}; + use rt::task::{Task, Unwinder}; + use rt::local::Local; + + let context = context(); + match context { + OldTaskContext => { + unsafe { + gc::cleanup_stack_for_failure(); + rustrt::rust_upcall_fail(msg, file, line); + cast::transmute(()) + } + } + _ => { + unsafe { + // XXX: Bad re-allocations. fail! needs some refactoring + let msg = str::raw::from_c_str(msg); + let file = str::raw::from_c_str(file); + + let outmsg = fmt!("%s at line %i of file %s", msg, line as int, file); + + // XXX: Logging doesn't work correctly in non-task context because it + // invokes the local heap + if context == TaskContext { + error!(outmsg); + } else { + rtdebug!("%s", outmsg); + } + + gc::cleanup_stack_for_failure(); + + let task = Local::unsafe_borrow::<Task>(); + let unwinder: &mut Option<Unwinder> = &mut (*task).unwinder; + match *unwinder { + Some(ref mut unwinder) => unwinder.begin_unwind(), + None => abort!("failure without unwinder. aborting process") + } + } + } + } +} + +#[cfg(test)] +mod tests { + use cast; + use sys::*; + + #[test] + fn size_of_basic() { + assert_eq!(size_of::<u8>(), 1u); + assert_eq!(size_of::<u16>(), 2u); + assert_eq!(size_of::<u32>(), 4u); + assert_eq!(size_of::<u64>(), 8u); + } + + #[test] + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + #[cfg(target_arch = "mips")] + fn size_of_32() { + assert_eq!(size_of::<uint>(), 4u); + assert_eq!(size_of::<*uint>(), 4u); + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn size_of_64() { + assert_eq!(size_of::<uint>(), 8u); + assert_eq!(size_of::<*uint>(), 8u); + } + + #[test] + fn size_of_val_basic() { + assert_eq!(size_of_val(&1u8), 1); + assert_eq!(size_of_val(&1u16), 2); + assert_eq!(size_of_val(&1u32), 4); + assert_eq!(size_of_val(&1u64), 8); + } + + #[test] + fn nonzero_size_of_basic() { + type Z = [i8, ..0]; + assert_eq!(size_of::<Z>(), 0u); + assert_eq!(nonzero_size_of::<Z>(), 1u); + assert_eq!(nonzero_size_of::<uint>(), size_of::<uint>()); + } + + #[test] + fn nonzero_size_of_val_basic() { + let z = [0u8, ..0]; + assert_eq!(size_of_val(&z), 0u); + assert_eq!(nonzero_size_of_val(&z), 1u); + assert_eq!(nonzero_size_of_val(&1u), size_of_val(&1u)); + } + + #[test] + fn align_of_basic() { + assert_eq!(pref_align_of::<u8>(), 1u); + assert_eq!(pref_align_of::<u16>(), 2u); + assert_eq!(pref_align_of::<u32>(), 4u); + } + + #[test] + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "arm")] + #[cfg(target_arch = "mips")] + fn align_of_32() { + assert_eq!(pref_align_of::<uint>(), 4u); + assert_eq!(pref_align_of::<*uint>(), 4u); + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn align_of_64() { + assert_eq!(pref_align_of::<uint>(), 8u); + assert_eq!(pref_align_of::<*uint>(), 8u); + } + + #[test] + fn align_of_val_basic() { + assert_eq!(pref_align_of_val(&1u8), 1u); + assert_eq!(pref_align_of_val(&1u16), 2u); + assert_eq!(pref_align_of_val(&1u32), 4u); + } + + #[test] + fn synthesize_closure() { + unsafe { + let x = 10; + let f: &fn(int) -> int = |y| x + y; + + assert_eq!(f(20), 30); + + let original_closure: Closure = cast::transmute(f); + + let actual_function_pointer = original_closure.code; + let environment = original_closure.env; + + let new_closure = Closure { + code: actual_function_pointer, + env: environment + }; + + let new_f: &fn(int) -> int = cast::transmute(new_closure); + assert_eq!(new_f(20), 30); + } + } + + #[test] + #[should_fail] + fn fail_static() { FailWithCause::fail_with("cause", file!(), line!()) } + + #[test] + #[should_fail] + fn fail_owned() { FailWithCause::fail_with(~"cause", file!(), line!()) } +} diff --git a/src/libstd/task/local_data_priv.rs b/src/libstd/task/local_data_priv.rs new file mode 100644 index 00000000000..2f97eaacf4b --- /dev/null +++ b/src/libstd/task/local_data_priv.rs @@ -0,0 +1,235 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; // FIXME #3538 + +use cast; +use cmp::Eq; +use libc; +use prelude::*; +use task::rt; +use local_data::LocalDataKey; + +use super::rt::rust_task; +use rt::task::{Task, LocalStorage}; + +pub enum Handle { + OldHandle(*rust_task), + NewHandle(*mut LocalStorage) +} + +impl Handle { + pub fn new() -> Handle { + use rt::{context, OldTaskContext}; + use rt::local::Local; + unsafe { + match context() { + OldTaskContext => { + OldHandle(rt::rust_get_task()) + } + _ => { + let task = Local::unsafe_borrow::<Task>(); + NewHandle(&mut (*task).storage) + } + } + } + } +} + +pub trait LocalData { } +impl<T: 'static> LocalData for @T { } + +impl Eq for @LocalData { + fn eq(&self, other: &@LocalData) -> bool { + unsafe { + let ptr_a: &(uint, uint) = cast::transmute(self); + let ptr_b: &(uint, uint) = cast::transmute(other); + return ptr_a == ptr_b; + } + } + fn ne(&self, other: &@LocalData) -> bool { !(*self).eq(other) } +} + +// If TLS is used heavily in future, this could be made more efficient with a +// proper map. +type TaskLocalElement = (*libc::c_void, *libc::c_void, @LocalData); +// Has to be a pointer at outermost layer; the foreign call returns void *. +type TaskLocalMap = @mut ~[Option<TaskLocalElement>]; + +fn cleanup_task_local_map(map_ptr: *libc::c_void) { + unsafe { + assert!(!map_ptr.is_null()); + // Get and keep the single reference that was created at the + // beginning. + let _map: TaskLocalMap = cast::transmute(map_ptr); + // All local_data will be destroyed along with the map. + } +} + +// Gets the map from the runtime. Lazily initialises if not done so already. +unsafe fn get_local_map(handle: Handle) -> TaskLocalMap { + match handle { + OldHandle(task) => get_task_local_map(task), + NewHandle(local_storage) => get_newsched_local_map(local_storage) + } +} + +unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { + + extern fn cleanup_task_local_map_extern_cb(map_ptr: *libc::c_void) { + cleanup_task_local_map(map_ptr); + } + + // Relies on the runtime initialising the pointer to null. + // Note: The map's box lives in TLS invisibly referenced once. Each time + // we retrieve it for get/set, we make another reference, which get/set + // drop when they finish. No "re-storing after modifying" is needed. + let map_ptr = rt::rust_get_task_local_data(task); + if map_ptr.is_null() { + let map: TaskLocalMap = @mut ~[]; + // NB: This bumps the ref count before converting to an unsafe pointer, + // keeping the map alive until TLS is destroyed + rt::rust_set_task_local_data(task, cast::transmute(map)); + rt::rust_task_local_data_atexit(task, cleanup_task_local_map_extern_cb); + map + } else { + let map = cast::transmute(map_ptr); + let nonmut = cast::transmute::<TaskLocalMap, + @~[Option<TaskLocalElement>]>(map); + cast::bump_box_refcount(nonmut); + map + } +} + +unsafe fn get_newsched_local_map(local: *mut LocalStorage) -> TaskLocalMap { + match &mut *local { + &LocalStorage(map_ptr, Some(_)) => { + assert!(map_ptr.is_not_null()); + let map = cast::transmute(map_ptr); + let nonmut = cast::transmute::<TaskLocalMap, + @~[Option<TaskLocalElement>]>(map); + cast::bump_box_refcount(nonmut); + return map; + } + &LocalStorage(ref mut map_ptr, ref mut at_exit) => { + assert!((*map_ptr).is_null()); + let map: TaskLocalMap = @mut ~[]; + *map_ptr = cast::transmute(map); + let at_exit_fn: ~fn(*libc::c_void) = |p|cleanup_task_local_map(p); + *at_exit = Some(at_exit_fn); + return map; + } + } +} + +unsafe fn key_to_key_value<T: 'static>(key: LocalDataKey<T>) -> *libc::c_void { + // Keys are closures, which are (fnptr,envptr) pairs. Use fnptr. + // Use reinterpret_cast -- transmute would leak (forget) the closure. + let pair: (*libc::c_void, *libc::c_void) = cast::transmute_copy(&key); + pair.first() +} + +// If returning Some(..), returns with @T with the map's reference. Careful! +unsafe fn local_data_lookup<T: 'static>( + map: TaskLocalMap, key: LocalDataKey<T>) + -> Option<(uint, *libc::c_void)> { + + let key_value = key_to_key_value(key); + let map_pos = (*map).position(|entry| + match *entry { + Some((k,_,_)) => k == key_value, + None => false + } + ); + do map_pos.map |index| { + // .get() is guaranteed because of "None { false }" above. + let (_, data_ptr, _) = (*map)[*index].get(); + (*index, data_ptr) + } +} + +unsafe fn local_get_helper<T: 'static>( + handle: Handle, key: LocalDataKey<T>, + do_pop: bool) -> Option<@T> { + + let map = get_local_map(handle); + // Interpreturn our findings from the map + do local_data_lookup(map, key).map |result| { + // A reference count magically appears on 'data' out of thin air. It + // was referenced in the local_data box, though, not here, so before + // overwriting the local_data_box we need to give an extra reference. + // We must also give an extra reference when not removing. + let (index, data_ptr) = *result; + let data: @T = cast::transmute(data_ptr); + cast::bump_box_refcount(data); + if do_pop { + map[index] = None; + } + data + } +} + + +pub unsafe fn local_pop<T: 'static>( + handle: Handle, + key: LocalDataKey<T>) -> Option<@T> { + + local_get_helper(handle, key, true) +} + +pub unsafe fn local_get<T: 'static>( + handle: Handle, + key: LocalDataKey<T>) -> Option<@T> { + + local_get_helper(handle, key, false) +} + +pub unsafe fn local_set<T: 'static>( + handle: Handle, key: LocalDataKey<T>, data: @T) { + + let map = get_local_map(handle); + // Store key+data as *voids. Data is invisibly referenced once; key isn't. + let keyval = key_to_key_value(key); + // We keep the data in two forms: one as an unsafe pointer, so we can get + // it back by casting; another in an existential box, so the reference we + // own on it can be dropped when the box is destroyed. The unsafe pointer + // does not have a reference associated with it, so it may become invalid + // when the box is destroyed. + let data_ptr = *cast::transmute::<&@T, &*libc::c_void>(&data); + let data_box = @data as @LocalData; + // Construct new entry to store in the map. + let new_entry = Some((keyval, data_ptr, data_box)); + // Find a place to put it. + match local_data_lookup(map, key) { + Some((index, _old_data_ptr)) => { + // Key already had a value set, _old_data_ptr, whose reference + // will get dropped when the local_data box is overwritten. + map[index] = new_entry; + } + None => { + // Find an empty slot. If not, grow the vector. + match (*map).position(|x| x.is_none()) { + Some(empty_index) => { map[empty_index] = new_entry; } + None => { map.push(new_entry); } + } + } + } +} + +pub unsafe fn local_modify<T: 'static>( + handle: Handle, key: LocalDataKey<T>, + modify_fn: &fn(Option<@T>) -> Option<@T>) { + + // Could be more efficient by doing the lookup work, but this is easy. + let newdata = modify_fn(local_pop(handle, key)); + if newdata.is_some() { + local_set(handle, key, newdata.unwrap()); + } +} diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs new file mode 100644 index 00000000000..490a69248ee --- /dev/null +++ b/src/libstd/task/mod.rs @@ -0,0 +1,1189 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + * Task management. + * + * An executing Rust program consists of a tree of tasks, each with their own + * stack, and sole ownership of their allocated heap data. Tasks communicate + * with each other using ports and channels. + * + * When a task fails, that failure will propagate to its parent (the task + * that spawned it) and the parent will fail as well. The reverse is not + * true: when a parent task fails its children will continue executing. When + * the root (main) task fails, all tasks fail, and then so does the entire + * process. + * + * Tasks may execute in parallel and are scheduled automatically by the + * runtime. + * + * # Example + * + * ~~~ + * do spawn { + * log(error, "Hello, World!"); + * } + * ~~~ + */ + +use cell::Cell; +use cmp::Eq; +use result::Result; +use comm::{stream, Chan, GenericChan, GenericPort, Port}; +use prelude::*; +use result; +use task::rt::{task_id, sched_id}; +use util; +use util::replace; +use unstable::finally::Finally; +use rt::{context, OldTaskContext}; + +#[cfg(test)] use comm::SharedChan; + +mod local_data_priv; +pub mod rt; +pub mod spawn; + +/// A handle to a scheduler +#[deriving(Eq)] +pub enum Scheduler { + SchedulerHandle(sched_id) +} + +/// A handle to a task +#[deriving(Eq)] +pub enum Task { + TaskHandle(task_id) +} + +/** + * Indicates the manner in which a task exited. + * + * A task that completes without failing is considered to exit successfully. + * Supervised ancestors and linked siblings may yet fail after this task + * succeeds. Also note that in such a case, it may be nondeterministic whether + * linked failure or successful exit happen first. + * + * If you wish for this result's delivery to block until all linked and/or + * children tasks complete, recommend using a result future. + */ +#[deriving(Eq)] +pub enum TaskResult { + Success, + Failure, +} + +/// Scheduler modes +#[deriving(Eq)] +pub enum SchedMode { + /// Run task on the default scheduler + DefaultScheduler, + /// Run task on the current scheduler + CurrentScheduler, + /// Run task on a specific scheduler + ExistingScheduler(Scheduler), + /** + * Tasks are scheduled on the main OS thread + * + * The main OS thread is the thread used to launch the runtime which, + * in most cases, is the process's initial thread as created by the OS. + */ + PlatformThread, + /// All tasks run in the same OS thread + SingleThreaded, + /// Tasks are distributed among available CPUs + ThreadPerCore, + /// Each task runs in its own OS thread + ThreadPerTask, + /// Tasks are distributed among a fixed number of OS threads + ManualThreads(uint), +} + +/** + * Scheduler configuration options + * + * # Fields + * + * * sched_mode - The operating mode of the scheduler + * + * * foreign_stack_size - The size of the foreign stack, in bytes + * + * Rust code runs on Rust-specific stacks. When Rust code calls foreign + * code (via functions in foreign modules) it switches to a typical, large + * stack appropriate for running code written in languages like C. By + * default these foreign stacks have unspecified size, but with this + * option their size can be precisely specified. + */ +pub struct SchedOpts { + mode: SchedMode, + foreign_stack_size: Option<uint>, +} + +/** + * Task configuration options + * + * # Fields + * + * * linked - Propagate failure bidirectionally between child and parent. + * True by default. If both this and 'supervised' are false, then + * either task's failure will not affect the other ("unlinked"). + * + * * supervised - Propagate failure unidirectionally from parent to child, + * but not from child to parent. False by default. + * + * * notify_chan - Enable lifecycle notifications on the given channel + * + * * sched - Specify the configuration of a new scheduler to create the task + * in + * + * By default, every task is created in the same scheduler as its + * parent, where it is scheduled cooperatively with all other tasks + * in that scheduler. Some specialized applications may want more + * control over their scheduling, in which case they can be spawned + * into a new scheduler with the specific properties required. + * + * This is of particular importance for libraries which want to call + * into foreign code that blocks. Without doing so in a different + * scheduler other tasks will be impeded or even blocked indefinitely. + */ +pub struct TaskOpts { + linked: bool, + supervised: bool, + notify_chan: Option<Chan<TaskResult>>, + sched: SchedOpts +} + +/** + * The task builder type. + * + * Provides detailed control over the properties and behavior of new tasks. + */ +// NB: Builders are designed to be single-use because they do stateful +// things that get weird when reusing - e.g. if you create a result future +// it only applies to a single task, so then you have to maintain Some +// potentially tricky state to ensure that everything behaves correctly +// when you try to reuse the builder to spawn a new task. We'll just +// sidestep that whole issue by making builders uncopyable and making +// the run function move them in. + +// FIXME (#3724): Replace the 'consumed' bit with move mode on self +pub struct TaskBuilder { + opts: TaskOpts, + gen_body: Option<~fn(v: ~fn()) -> ~fn()>, + can_not_copy: Option<util::NonCopyable>, + consumed: bool, +} + +/** + * Generate the base configuration for spawning a task, off of which more + * configuration methods can be chained. + * For example, task().unlinked().spawn is equivalent to spawn_unlinked. + */ +pub fn task() -> TaskBuilder { + TaskBuilder { + opts: default_task_opts(), + gen_body: None, + can_not_copy: None, + consumed: false, + } +} + +#[doc(hidden)] // FIXME #3538 +priv impl TaskBuilder { + fn consume(&mut self) -> TaskBuilder { + if self.consumed { + fail!("Cannot copy a task_builder"); // Fake move mode on self + } + self.consumed = true; + let gen_body = replace(&mut self.gen_body, None); + let notify_chan = replace(&mut self.opts.notify_chan, None); + TaskBuilder { + opts: TaskOpts { + linked: self.opts.linked, + supervised: self.opts.supervised, + notify_chan: notify_chan, + sched: self.opts.sched + }, + gen_body: gen_body, + can_not_copy: None, + consumed: false + } + } +} + +pub impl TaskBuilder { + /// Decouple the child task's failure from the parent's. If either fails, + /// the other will not be killed. + fn unlinked(&mut self) { + self.opts.linked = false; + } + + /// Unidirectionally link the child task's failure with the parent's. The + /// child's failure will not kill the parent, but the parent's will kill + /// the child. + fn supervised(&mut self) { + self.opts.supervised = true; + self.opts.linked = false; + } + + /// Link the child task's and parent task's failures. If either fails, the + /// other will be killed. + fn linked(&mut self) { + self.opts.linked = true; + self.opts.supervised = false; + } + + /** + * Get a future representing the exit status of the task. + * + * Taking the value of the future will block until the child task + * terminates. The future-receiving callback specified will be called + * *before* the task is spawned; as such, do not invoke .get() within the + * closure; rather, store it in an outer variable/list for later use. + * + * Note that the future returning by this function is only useful for + * obtaining the value of the next task to be spawning with the + * builder. If additional tasks are spawned with the same builder + * then a new result future must be obtained prior to spawning each + * task. + * + * # Failure + * Fails if a future_result was already set for this task. + */ + fn future_result(&mut self, blk: &fn(v: Port<TaskResult>)) { + // FIXME (#3725): Once linked failure and notification are + // handled in the library, I can imagine implementing this by just + // registering an arbitrary number of task::on_exit handlers and + // sending out messages. + + if self.opts.notify_chan.is_some() { + fail!("Can't set multiple future_results for one task!"); + } + + // Construct the future and give it to the caller. + let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>(); + + blk(notify_pipe_po); + + // Reconfigure self to use a notify channel. + self.opts.notify_chan = Some(notify_pipe_ch); + } + + /// Configure a custom scheduler mode for the task. + fn sched_mode(&mut self, mode: SchedMode) { + self.opts.sched.mode = mode; + } + + /** + * Add a wrapper to the body of the spawned task. + * + * Before the task is spawned it is passed through a 'body generator' + * function that may perform local setup operations as well as wrap + * the task body in remote setup operations. With this the behavior + * of tasks can be extended in simple ways. + * + * This function augments the current body generator with a new body + * generator by applying the task body which results from the + * existing body generator to the new body generator. + */ + fn add_wrapper(&mut self, wrapper: ~fn(v: ~fn()) -> ~fn()) { + let prev_gen_body = replace(&mut self.gen_body, None); + let prev_gen_body = match prev_gen_body { + Some(gen) => gen, + None => { + let f: ~fn(~fn()) -> ~fn() = |body| body; + f + } + }; + let prev_gen_body = Cell(prev_gen_body); + let next_gen_body = { + let f: ~fn(~fn()) -> ~fn() = |body| { + let prev_gen_body = prev_gen_body.take(); + wrapper(prev_gen_body(body)) + }; + f + }; + self.gen_body = Some(next_gen_body); + } + + /** + * Creates and executes a new child task + * + * Sets up a new task with its own call stack and schedules it to run + * the provided unique closure. The task has the properties and behavior + * specified by the task_builder. + * + * # Failure + * + * When spawning into a new scheduler, the number of threads requested + * must be greater than zero. + */ + fn spawn(&mut self, f: ~fn()) { + let gen_body = replace(&mut self.gen_body, None); + let notify_chan = replace(&mut self.opts.notify_chan, None); + let x = self.consume(); + let opts = TaskOpts { + linked: x.opts.linked, + supervised: x.opts.supervised, + notify_chan: notify_chan, + sched: x.opts.sched + }; + let f = match gen_body { + Some(gen) => { + gen(f) + } + None => { + f + } + }; + spawn::spawn_raw(opts, f); + } + + /// Runs a task, while transfering ownership of one argument to the child. + fn spawn_with<A:Owned>(&mut self, arg: A, f: ~fn(v: A)) { + let arg = Cell(arg); + do self.spawn { + f(arg.take()); + } + } + + /** + * Execute a function in another task and return either the return value + * of the function or result::err. + * + * # Return value + * + * If the function executed successfully then try returns result::ok + * containing the value returned by the function. If the function fails + * then try returns result::err containing nil. + * + * # Failure + * Fails if a future_result was already set for this task. + */ + fn try<T:Owned>(&mut self, f: ~fn() -> T) -> Result<T,()> { + let (po, ch) = stream::<T>(); + let mut result = None; + + self.future_result(|r| { result = Some(r); }); + + do self.spawn { + ch.send(f()); + } + + match result.unwrap().recv() { + Success => result::Ok(po.recv()), + Failure => result::Err(()) + } + } +} + + +/* Task construction */ + +pub fn default_task_opts() -> TaskOpts { + /*! + * The default task options + * + * By default all tasks are supervised by their parent, are spawned + * into the same scheduler, and do not post lifecycle notifications. + */ + + TaskOpts { + linked: true, + supervised: false, + notify_chan: None, + sched: SchedOpts { + mode: DefaultScheduler, + foreign_stack_size: None + } + } +} + +/* Spawn convenience functions */ + +/// Creates and executes a new child task +/// +/// Sets up a new task with its own call stack and schedules it to run +/// the provided unique closure. +/// +/// This function is equivalent to `task().spawn(f)`. +pub fn spawn(f: ~fn()) { + let mut task = task(); + task.spawn(f) +} + +/// Creates a child task unlinked from the current one. If either this +/// task or the child task fails, the other will not be killed. +pub fn spawn_unlinked(f: ~fn()) { + let mut task = task(); + task.unlinked(); + task.spawn(f) +} + +pub fn spawn_supervised(f: ~fn()) { + /*! + * Creates a child task supervised by the current one. If the child + * task fails, the parent will not be killed, but if the parent fails, + * the child will be killed. + */ + + let mut task = task(); + task.supervised(); + task.spawn(f) +} + +pub fn spawn_with<A:Owned>(arg: A, f: ~fn(v: A)) { + /*! + * Runs a task, while transfering ownership of one argument to the + * child. + * + * This is useful for transfering ownership of noncopyables to + * another task. + * + * This function is equivalent to `task().spawn_with(arg, f)`. + */ + + let mut task = task(); + task.spawn_with(arg, f) +} + +pub fn spawn_sched(mode: SchedMode, f: ~fn()) { + /*! + * Creates a new task on a new or existing scheduler + + * When there are no more tasks to execute the + * scheduler terminates. + * + * # Failure + * + * In manual threads mode the number of threads requested must be + * greater than zero. + */ + + let mut task = task(); + task.sched_mode(mode); + task.spawn(f) +} + +pub fn try<T:Owned>(f: ~fn() -> T) -> Result<T,()> { + /*! + * Execute a function in another task and return either the return value + * of the function or result::err. + * + * This is equivalent to task().supervised().try. + */ + + let mut task = task(); + task.supervised(); + task.try(f) +} + + +/* Lifecycle functions */ + +pub fn yield() { + //! Yield control to the task scheduler + + unsafe { + let task_ = rt::rust_get_task(); + let killed = rt::rust_task_yield(task_); + if killed && !failing() { + fail!("killed"); + } + } +} + +pub fn failing() -> bool { + //! True if the running task has failed + + use rt::{context, OldTaskContext}; + use rt::local::Local; + use rt::task::Task; + + match context() { + OldTaskContext => { + unsafe { + rt::rust_task_is_unwinding(rt::rust_get_task()) + } + } + _ => { + let mut unwinding = false; + do Local::borrow::<Task> |local| { + unwinding = match local.unwinder { + Some(unwinder) => { + unwinder.unwinding + } + None => { + // Because there is no unwinder we can't be unwinding. + // (The process will abort on failure) + false + } + } + } + return unwinding; + } + } +} + +pub fn get_task() -> Task { + //! Get a handle to the running task + + unsafe { + TaskHandle(rt::get_task_id()) + } +} + +pub fn get_scheduler() -> Scheduler { + SchedulerHandle(unsafe { rt::rust_get_sched_id() }) +} + +/** + * Temporarily make the task unkillable + * + * # Example + * + * ~~~ + * do task::unkillable { + * // detach / yield / destroy must all be called together + * rustrt::rust_port_detach(po); + * // This must not result in the current task being killed + * task::yield(); + * rustrt::rust_port_destroy(po); + * } + * ~~~ + */ +pub unsafe fn unkillable<U>(f: &fn() -> U) -> U { + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_inhibit_kill(t); + f() + }).finally { + rt::rust_task_allow_kill(t); + } + } else { + // FIXME #6377 + f() + } +} + +/// The inverse of unkillable. Only ever to be used nested in unkillable(). +pub unsafe fn rekillable<U>(f: &fn() -> U) -> U { + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_allow_kill(t); + f() + }).finally { + rt::rust_task_inhibit_kill(t); + } + } else { + // FIXME #6377 + f() + } +} + +/** + * A stronger version of unkillable that also inhibits scheduling operations. + * For use with exclusive ARCs, which use pthread mutexes directly. + */ +pub unsafe fn atomically<U>(f: &fn() -> U) -> U { + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_inhibit_kill(t); + rt::rust_task_inhibit_yield(t); + f() + }).finally { + rt::rust_task_allow_yield(t); + rt::rust_task_allow_kill(t); + } + } else { + // FIXME #6377 + f() + } +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_cant_dup_task_builder() { + let mut builder = task(); + builder.unlinked(); + do builder.spawn {} + // FIXME(#3724): For now, this is a -runtime- failure, because we haven't + // got move mode on self. When 3724 is fixed, this test should fail to + // compile instead, and should go in tests/compile-fail. + do builder.spawn {} // b should have been consumed by the previous call +} + +// The following 8 tests test the following 2^3 combinations: +// {un,}linked {un,}supervised failure propagation {up,down}wards. + +// !!! These tests are dangerous. If Something is buggy, they will hang, !!! +// !!! instead of exiting cleanly. This might wedge the buildbots. !!! + +#[test] #[ignore(cfg(windows))] +fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port + let (po, ch) = stream(); + let ch = SharedChan::new(ch); + do spawn_unlinked { + let ch = ch.clone(); + do spawn_unlinked { + // Give middle task a chance to fail-but-not-kill-us. + for 16.times { task::yield(); } + ch.send(()); // If killed first, grandparent hangs. + } + fail!(); // Shouldn't kill either (grand)parent or (grand)child. + } + po.recv(); +} +#[test] #[ignore(cfg(windows))] +fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails + do spawn_unlinked { fail!(); } +} +#[test] #[ignore(cfg(windows))] +fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails + do spawn_supervised { fail!(); } + // Give child a chance to fail-but-not-kill-us. + for 16.times { task::yield(); } +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_unlinked_sup_fail_down() { + do spawn_supervised { loop { task::yield(); } } + fail!(); // Shouldn't leave a child hanging around. +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_sup_fail_up() { // child fails; parent fails + let (po, _ch) = stream::<()>(); + + // Unidirectional "parenting" shouldn't override bidirectional linked. + // We have to cheat with opts - the interface doesn't support them because + // they don't make sense (redundant with task().supervised()). + let mut b0 = task(); + b0.opts.linked = true; + b0.opts.supervised = true; + + do b0.spawn { + fail!(); + } + po.recv(); // We should get punted awake +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_sup_fail_down() { // parent fails; child fails + // We have to cheat with opts - the interface doesn't support them because + // they don't make sense (redundant with task().supervised()). + let mut b0 = task(); + b0.opts.linked = true; + b0.opts.supervised = true; + do b0.spawn { + loop { + task::yield(); + } + } + fail!(); // *both* mechanisms would be wrong if this didn't kill the child +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails + let (po, _ch) = stream::<()>(); + // Default options are to spawn linked & unsupervised. + do spawn { fail!(); } + po.recv(); // We should get punted awake +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails + // Default options are to spawn linked & unsupervised. + do spawn { loop { task::yield(); } } + fail!(); +} +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails + // Make sure the above test is the same as this one. + let mut builder = task(); + builder.linked(); + do builder.spawn { + loop { + task::yield(); + } + } + fail!(); +} + +// A couple bonus linked failure tests - testing for failure propagation even +// when the middle task exits successfully early before kill signals are sent. + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_failure_propagate_grandchild() { + // Middle task exits; does grandparent's failure propagate across the gap? + do spawn_supervised { + do spawn_supervised { + loop { task::yield(); } + } + } + for 16.times { task::yield(); } + fail!(); +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_failure_propagate_secondborn() { + // First-born child exits; does parent's failure propagate to sibling? + do spawn_supervised { + do spawn { // linked + loop { task::yield(); } + } + } + for 16.times { task::yield(); } + fail!(); +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_failure_propagate_nephew_or_niece() { + // Our sibling exits; does our failure propagate to sibling's child? + do spawn { // linked + do spawn_supervised { + loop { task::yield(); } + } + } + for 16.times { task::yield(); } + fail!(); +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_spawn_linked_sup_propagate_sibling() { + // Middle sibling exits - does eldest's failure propagate to youngest? + do spawn { // linked + do spawn { // linked + loop { task::yield(); } + } + } + for 16.times { task::yield(); } + fail!(); +} + +#[test] +fn test_run_basic() { + let (po, ch) = stream::<()>(); + let mut builder = task(); + do builder.spawn { + ch.send(()); + } + po.recv(); +} + +#[cfg(test)] +struct Wrapper { + f: Option<Chan<()>> +} + +#[test] +fn test_add_wrapper() { + let (po, ch) = stream::<()>(); + let mut b0 = task(); + let ch = Cell(ch); + do b0.add_wrapper |body| { + let ch = Cell(ch.take()); + let result: ~fn() = || { + let ch = ch.take(); + body(); + ch.send(()); + }; + result + }; + do b0.spawn { } + po.recv(); +} + +#[test] +#[ignore(cfg(windows))] +fn test_future_result() { + let mut result = None; + let mut builder = task(); + builder.future_result(|r| result = Some(r)); + do builder.spawn {} + assert_eq!(result.unwrap().recv(), Success); + + result = None; + let mut builder = task(); + builder.future_result(|r| result = Some(r)); + builder.unlinked(); + do builder.spawn { + fail!(); + } + assert_eq!(result.unwrap().recv(), Failure); +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_back_to_the_future_result() { + let mut builder = task(); + builder.future_result(util::ignore); + builder.future_result(util::ignore); +} + +#[test] +fn test_try_success() { + match do try { + ~"Success!" + } { + result::Ok(~"Success!") => (), + _ => fail!() + } +} + +#[test] +#[ignore(cfg(windows))] +fn test_try_fail() { + match do try { + fail!() + } { + result::Err(()) => (), + result::Ok(()) => fail!() + } +} + +#[test] +#[should_fail] +#[ignore(cfg(windows))] +fn test_spawn_sched_no_threads() { + do spawn_sched(ManualThreads(0u)) { } +} + +#[test] +fn test_spawn_sched() { + let (po, ch) = stream::<()>(); + let ch = SharedChan::new(ch); + + fn f(i: int, ch: SharedChan<()>) { + let parent_sched_id = unsafe { rt::rust_get_sched_id() }; + + do spawn_sched(SingleThreaded) { + let child_sched_id = unsafe { rt::rust_get_sched_id() }; + assert!(parent_sched_id != child_sched_id); + + if (i == 0) { + ch.send(()); + } else { + f(i - 1, ch.clone()); + } + }; + + } + f(10, ch); + po.recv(); +} + +#[test] +fn test_spawn_sched_childs_on_default_sched() { + let (po, ch) = stream(); + + // Assuming tests run on the default scheduler + let default_id = unsafe { rt::rust_get_sched_id() }; + + let ch = Cell(ch); + do spawn_sched(SingleThreaded) { + let parent_sched_id = unsafe { rt::rust_get_sched_id() }; + let ch = Cell(ch.take()); + do spawn { + let ch = ch.take(); + let child_sched_id = unsafe { rt::rust_get_sched_id() }; + assert!(parent_sched_id != child_sched_id); + assert_eq!(child_sched_id, default_id); + ch.send(()); + }; + }; + + po.recv(); +} + +#[cfg(test)] +mod testrt { + use libc; + + #[nolink] + pub extern { + unsafe fn rust_dbg_lock_create() -> *libc::c_void; + unsafe fn rust_dbg_lock_destroy(lock: *libc::c_void); + unsafe fn rust_dbg_lock_lock(lock: *libc::c_void); + unsafe fn rust_dbg_lock_unlock(lock: *libc::c_void); + unsafe fn rust_dbg_lock_wait(lock: *libc::c_void); + unsafe fn rust_dbg_lock_signal(lock: *libc::c_void); + } +} + +#[test] +fn test_spawn_sched_blocking() { + unsafe { + + // Testing that a task in one scheduler can block in foreign code + // without affecting other schedulers + for 20u.times { + let (start_po, start_ch) = stream(); + let (fin_po, fin_ch) = stream(); + + let lock = testrt::rust_dbg_lock_create(); + + do spawn_sched(SingleThreaded) { + unsafe { + testrt::rust_dbg_lock_lock(lock); + + start_ch.send(()); + + // Block the scheduler thread + testrt::rust_dbg_lock_wait(lock); + testrt::rust_dbg_lock_unlock(lock); + + fin_ch.send(()); + } + }; + + // Wait until the other task has its lock + start_po.recv(); + + fn pingpong(po: &Port<int>, ch: &Chan<int>) { + let mut val = 20; + while val > 0 { + val = po.recv(); + ch.send(val - 1); + } + } + + let (setup_po, setup_ch) = stream(); + let (parent_po, parent_ch) = stream(); + do spawn { + let (child_po, child_ch) = stream(); + setup_ch.send(child_ch); + pingpong(&child_po, &parent_ch); + }; + + let child_ch = setup_po.recv(); + child_ch.send(20); + pingpong(&parent_po, &child_ch); + testrt::rust_dbg_lock_lock(lock); + testrt::rust_dbg_lock_signal(lock); + testrt::rust_dbg_lock_unlock(lock); + fin_po.recv(); + testrt::rust_dbg_lock_destroy(lock); + } + } +} + +#[cfg(test)] +fn avoid_copying_the_body(spawnfn: &fn(v: ~fn())) { + let (p, ch) = stream::<uint>(); + + let x = ~1; + let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint; + + do spawnfn || { + let x_in_child = ptr::to_unsafe_ptr(&*x) as uint; + ch.send(x_in_child); + } + + let x_in_child = p.recv(); + assert_eq!(x_in_parent, x_in_child); +} + +#[test] +fn test_avoid_copying_the_body_spawn() { + avoid_copying_the_body(spawn); +} + +#[test] +fn test_avoid_copying_the_body_task_spawn() { + do avoid_copying_the_body |f| { + let mut builder = task(); + do builder.spawn || { + f(); + } + } +} + +#[test] +fn test_avoid_copying_the_body_try() { + do avoid_copying_the_body |f| { + do try || { + f() + }; + } +} + +#[test] +fn test_avoid_copying_the_body_unlinked() { + do avoid_copying_the_body |f| { + do spawn_unlinked || { + f(); + } + } +} + +#[test] +fn test_platform_thread() { + let (po, ch) = stream(); + let mut builder = task(); + builder.sched_mode(PlatformThread); + do builder.spawn { + ch.send(()); + } + po.recv(); +} + +#[test] +#[ignore(cfg(windows))] +#[should_fail] +fn test_unkillable() { + let (po, ch) = stream(); + + // We want to do this after failing + do spawn_unlinked { + for 10.times { yield() } + ch.send(()); + } + + do spawn { + yield(); + // We want to fail after the unkillable task + // blocks on recv + fail!(); + } + + unsafe { + do unkillable { + let p = ~0; + let pp: *uint = cast::transmute(p); + + // If we are killed here then the box will leak + po.recv(); + + let _p: ~int = cast::transmute(pp); + } + } + + // Now we can be killed + po.recv(); +} + +#[test] +#[ignore(cfg(windows))] +#[should_fail] +fn test_unkillable_nested() { + let (po, ch) = comm::stream(); + + // We want to do this after failing + do spawn_unlinked || { + for 10.times { yield() } + ch.send(()); + } + + do spawn { + yield(); + // We want to fail after the unkillable task + // blocks on recv + fail!(); + } + + unsafe { + do unkillable { + do unkillable {} // Here's the difference from the previous test. + let p = ~0; + let pp: *uint = cast::transmute(p); + + // If we are killed here then the box will leak + po.recv(); + + let _p: ~int = cast::transmute(pp); + } + } + + // Now we can be killed + po.recv(); +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_atomically() { + unsafe { do atomically { yield(); } } +} + +#[test] +fn test_atomically2() { + unsafe { do atomically { } } yield(); // shouldn't fail +} + +#[test] #[should_fail] #[ignore(cfg(windows))] +fn test_atomically_nested() { + unsafe { do atomically { do atomically { } yield(); } } +} + +#[test] +fn test_child_doesnt_ref_parent() { + // If the child refcounts the parent task, this will stack overflow when + // climbing the task tree to dereference each ancestor. (See #1789) + // (well, it would if the constant were 8000+ - I lowered it to be more + // valgrind-friendly. try this at home, instead..!) + static generations: uint = 16; + fn child_no(x: uint) -> ~fn() { + return || { + if x < generations { + task::spawn(child_no(x+1)); + } + } + } + task::spawn(child_no(0)); +} + +#[test] +fn test_sched_thread_per_core() { + let (port, chan) = comm::stream(); + + do spawn_sched(ThreadPerCore) || { + unsafe { + let cores = rt::rust_num_threads(); + let reported_threads = rt::rust_sched_threads(); + assert_eq!(cores as uint, reported_threads as uint); + chan.send(()); + } + } + + port.recv(); +} + +#[test] +fn test_spawn_thread_on_demand() { + let (port, chan) = comm::stream(); + + do spawn_sched(ManualThreads(2)) || { + unsafe { + let max_threads = rt::rust_sched_threads(); + assert_eq!(max_threads as int, 2); + let running_threads = rt::rust_sched_current_nonlazy_threads(); + assert_eq!(running_threads as int, 1); + + let (port2, chan2) = comm::stream(); + + do spawn_sched(CurrentScheduler) || { + chan2.send(()); + } + + let running_threads2 = rt::rust_sched_current_nonlazy_threads(); + assert_eq!(running_threads2 as int, 2); + + port2.recv(); + chan.send(()); + } + } + + port.recv(); +} + +#[test] +fn test_simple_newsched_spawn() { + use rt::test::run_in_newsched_task; + + do run_in_newsched_task { + spawn(||()) + } +} diff --git a/src/libstd/task/rt.rs b/src/libstd/task/rt.rs new file mode 100644 index 00000000000..760812252bc --- /dev/null +++ b/src/libstd/task/rt.rs @@ -0,0 +1,71 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +The task interface to the runtime + +*/ + +#[doc(hidden)]; // FIXME #3538 + +use libc; + +#[allow(non_camel_case_types)] // runtime type +pub type sched_id = int; +#[allow(non_camel_case_types)] // runtime type +pub type task_id = int; + +// These are both opaque runtime/compiler types that we don't know the +// structure of and should only deal with via unsafe pointer +#[allow(non_camel_case_types)] // runtime type +pub type rust_task = libc::c_void; +#[allow(non_camel_case_types)] // runtime type +pub type rust_closure = libc::c_void; + +pub extern { + #[rust_stack] + fn rust_task_yield(task: *rust_task) -> bool; + + fn rust_get_sched_id() -> sched_id; + fn rust_new_sched(num_threads: libc::uintptr_t) -> sched_id; + fn rust_sched_threads() -> libc::size_t; + fn rust_sched_current_nonlazy_threads() -> libc::size_t; + fn rust_num_threads() -> libc::uintptr_t; + + fn get_task_id() -> task_id; + #[rust_stack] + fn rust_get_task() -> *rust_task; + + fn new_task() -> *rust_task; + fn rust_new_task_in_sched(id: sched_id) -> *rust_task; + + fn start_task(task: *rust_task, closure: *rust_closure); + + fn rust_task_is_unwinding(task: *rust_task) -> bool; + fn rust_osmain_sched_id() -> sched_id; + #[rust_stack] + fn rust_task_inhibit_kill(t: *rust_task); + #[rust_stack] + fn rust_task_allow_kill(t: *rust_task); + #[rust_stack] + fn rust_task_inhibit_yield(t: *rust_task); + #[rust_stack] + fn rust_task_allow_yield(t: *rust_task); + fn rust_task_kill_other(task: *rust_task); + fn rust_task_kill_all(task: *rust_task); + + #[rust_stack] + fn rust_get_task_local_data(task: *rust_task) -> *libc::c_void; + #[rust_stack] + fn rust_set_task_local_data(task: *rust_task, map: *libc::c_void); + #[rust_stack] + fn rust_task_local_data_atexit(task: *rust_task, cleanup_fn: *u8); +} diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs new file mode 100644 index 00000000000..81e5af5caab --- /dev/null +++ b/src/libstd/task/spawn.rs @@ -0,0 +1,791 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*!************************************************************************** + * Spawning & linked failure + * + * Several data structures are involved in task management to allow properly + * propagating failure across linked/supervised tasks. + * + * (1) The "taskgroup_arc" is an unsafe::exclusive which contains a hashset of + * all tasks that are part of the group. Some tasks are 'members', which + * means if they fail, they will kill everybody else in the taskgroup. + * Other tasks are 'descendants', which means they will not kill tasks + * from this group, but can be killed by failing members. + * + * A new one of these is created each spawn_linked or spawn_supervised. + * + * (2) The "tcb" is a per-task control structure that tracks a task's spawn + * configuration. It contains a reference to its taskgroup_arc, a + * reference to its node in the ancestor list (below), a flag for + * whether it's part of the 'main'/'root' taskgroup, and an optionally + * configured notification port. These are stored in TLS. + * + * (3) The "ancestor_list" is a cons-style list of unsafe::exclusives which + * tracks 'generations' of taskgroups -- a group's ancestors are groups + * which (directly or transitively) spawn_supervised-ed them. Each task + * is recorded in the 'descendants' of each of its ancestor groups. + * + * Spawning a supervised task is O(n) in the number of generations still + * alive, and exiting (by success or failure) that task is also O(n). + * + * This diagram depicts the references between these data structures: + * + * linked_________________________________ + * ___/ _________ \___ + * / \ | group X | / \ + * ( A ) - - - - - - - > | {A,B} {}|< - - -( B ) + * \___/ |_________| \___/ + * unlinked + * | __ (nil) + * | //| The following code causes this: + * |__ // /\ _________ + * / \ // || | group Y | fn taskA() { + * ( C )- - - ||- - - > |{C} {D,E}| spawn(taskB); + * \___/ / \=====> |_________| spawn_unlinked(taskC); + * supervise /gen \ ... + * | __ \ 00 / } + * | //| \__/ fn taskB() { ... } + * |__ // /\ _________ fn taskC() { + * / \/ || | group Z | spawn_supervised(taskD); + * ( D )- - - ||- - - > | {D} {E} | ... + * \___/ / \=====> |_________| } + * supervise /gen \ fn taskD() { + * | __ \ 01 / spawn_supervised(taskE); + * | //| \__/ ... + * |__ // _________ } + * / \/ | group W | fn taskE() { ... } + * ( E )- - - - - - - > | {E} {} | + * \___/ |_________| + * + * "tcb" "taskgroup_arc" + * "ancestor_list" + * + ****************************************************************************/ + +#[doc(hidden)]; // FIXME #3538 + +use cast::transmute; +use cast; +use cell::Cell; +use container::Map; +use comm::{Chan, GenericChan}; +use prelude::*; +use ptr; +use hashmap::HashSet; +use task::local_data_priv::{local_get, local_set, OldHandle}; +use task::rt::rust_task; +use task::rt; +use task::{Failure, ManualThreads, PlatformThread, SchedOpts, SingleThreaded}; +use task::{Success, TaskOpts, TaskResult, ThreadPerCore, ThreadPerTask}; +use task::{ExistingScheduler, SchedulerHandle}; +use task::unkillable; +use uint; +use util; +use unstable::sync::{Exclusive, exclusive}; +use rt::local::Local; + +#[cfg(test)] use task::default_task_opts; + +macro_rules! move_it ( + { $x:expr } => ( unsafe { let y = *ptr::to_unsafe_ptr(&($x)); y } ) +) + +type TaskSet = HashSet<*rust_task>; + +fn new_taskset() -> TaskSet { + HashSet::new() +} +fn taskset_insert(tasks: &mut TaskSet, task: *rust_task) { + let didnt_overwrite = tasks.insert(task); + assert!(didnt_overwrite); +} +fn taskset_remove(tasks: &mut TaskSet, task: *rust_task) { + let was_present = tasks.remove(&task); + assert!(was_present); +} +pub fn taskset_each(tasks: &TaskSet, blk: &fn(v: *rust_task) -> bool) -> bool { + tasks.each(|k| blk(*k)) +} + +// One of these per group of linked-failure tasks. +struct TaskGroupData { + // All tasks which might kill this group. When this is empty, the group + // can be "GC"ed (i.e., its link in the ancestor list can be removed). + members: TaskSet, + // All tasks unidirectionally supervised by (directly or transitively) + // tasks in this group. + descendants: TaskSet, +} +type TaskGroupArc = Exclusive<Option<TaskGroupData>>; + +type TaskGroupInner<'self> = &'self mut Option<TaskGroupData>; + +// A taskgroup is 'dead' when nothing can cause it to fail; only members can. +fn taskgroup_is_dead(tg: &TaskGroupData) -> bool { + (&const tg.members).is_empty() +} + +// A list-like structure by which taskgroups keep track of all ancestor groups +// which may kill them. Needed for tasks to be able to remove themselves from +// ancestor groups upon exit. The list has a node for each "generation", and +// ends either at the root taskgroup (which has no ancestors) or at a +// taskgroup which was spawned-unlinked. Tasks from intermediate generations +// have references to the middle of the list; when intermediate generations +// die, their node in the list will be collected at a descendant's spawn-time. +struct AncestorNode { + // Since the ancestor list is recursive, we end up with references to + // exclusives within other exclusives. This is dangerous business (if + // circular references arise, deadlock and memory leaks are imminent). + // Hence we assert that this counter monotonically decreases as we + // approach the tail of the list. + // FIXME(#3068): Make the generation counter togglable with #[cfg(debug)]. + generation: uint, + // Should really be a non-option. This way appeases borrowck. + parent_group: Option<TaskGroupArc>, + // Recursive rest of the list. + ancestors: AncestorList, +} + +struct AncestorList(Option<Exclusive<AncestorNode>>); + +// Accessors for taskgroup arcs and ancestor arcs that wrap the unsafety. +#[inline(always)] +fn access_group<U>(x: &TaskGroupArc, blk: &fn(TaskGroupInner) -> U) -> U { + x.with(blk) +} + +#[inline(always)] +fn access_ancestors<U>(x: &Exclusive<AncestorNode>, + blk: &fn(x: &mut AncestorNode) -> U) -> U { + x.with(blk) +} + +// Iterates over an ancestor list. +// (1) Runs forward_blk on each ancestral taskgroup in the list +// (2) If forward_blk "break"s, runs optional bail_blk on all ancestral +// taskgroups that forward_blk already ran on successfully (Note: bail_blk +// is NOT called on the block that forward_blk broke on!). +// (3) As a bonus, coalesces away all 'dead' taskgroup nodes in the list. +// FIXME(#2190): Change Option<@fn(...)> to Option<&fn(...)>, to save on +// allocations. Once that bug is fixed, changing the sigil should suffice. +fn each_ancestor(list: &mut AncestorList, + bail_opt: Option<@fn(TaskGroupInner)>, + forward_blk: &fn(TaskGroupInner) -> bool) + -> bool { + // "Kickoff" call - there was no last generation. + return !coalesce(list, bail_opt, forward_blk, uint::max_value); + + // Recursively iterates, and coalesces afterwards if needed. Returns + // whether or not unwinding is needed (i.e., !successful iteration). + fn coalesce(list: &mut AncestorList, + bail_opt: Option<@fn(TaskGroupInner)>, + forward_blk: &fn(TaskGroupInner) -> bool, + last_generation: uint) -> bool { + // Need to swap the list out to use it, to appease borrowck. + let tmp_list = util::replace(&mut *list, AncestorList(None)); + let (coalesce_this, early_break) = + iterate(&tmp_list, bail_opt, forward_blk, last_generation); + // What should our next ancestor end up being? + if coalesce_this.is_some() { + // Needed coalesce. Our next ancestor becomes our old + // ancestor's next ancestor. ("next = old_next->next;") + *list = coalesce_this.unwrap(); + } else { + // No coalesce; restore from tmp. ("next = old_next;") + *list = tmp_list; + } + return early_break; + } + + // Returns an optional list-to-coalesce and whether unwinding is needed. + // Option<ancestor_list>: + // Whether or not the ancestor taskgroup being iterated over is + // dead or not; i.e., it has no more tasks left in it, whether or not + // it has descendants. If dead, the caller shall coalesce it away. + // bool: + // True if the supplied block did 'break', here or in any recursive + // calls. If so, must call the unwinder on all previous nodes. + fn iterate(ancestors: &AncestorList, + bail_opt: Option<@fn(TaskGroupInner)>, + forward_blk: &fn(TaskGroupInner) -> bool, + last_generation: uint) + -> (Option<AncestorList>, bool) { + // At each step of iteration, three booleans are at play which govern + // how the iteration should behave. + // 'nobe_is_dead' - Should the list should be coalesced at this point? + // Largely unrelated to the other two. + // 'need_unwind' - Should we run the bail_blk at this point? (i.e., + // do_continue was false not here, but down the line) + // 'do_continue' - Did the forward_blk succeed at this point? (i.e., + // should we recurse? or should our callers unwind?) + + // The map defaults to None, because if ancestors is None, we're at + // the end of the list, which doesn't make sense to coalesce. + return do (**ancestors).map_default((None,false)) |ancestor_arc| { + // NB: Takes a lock! (this ancestor node) + do access_ancestors(ancestor_arc) |nobe| { + // Check monotonicity + assert!(last_generation > nobe.generation); + /*##########################################################* + * Step 1: Look at this ancestor group (call iterator block). + *##########################################################*/ + let mut nobe_is_dead = false; + let do_continue = + // NB: Takes a lock! (this ancestor node's parent group) + do with_parent_tg(&mut nobe.parent_group) |tg_opt| { + // Decide whether this group is dead. Note that the + // group being *dead* is disjoint from it *failing*. + nobe_is_dead = match *tg_opt { + Some(ref tg) => taskgroup_is_dead(tg), + None => nobe_is_dead + }; + // Call iterator block. (If the group is dead, it's + // safe to skip it. This will leave our *rust_task + // hanging around in the group even after it's freed, + // but that's ok because, by virtue of the group being + // dead, nobody will ever kill-all (foreach) over it.) + if nobe_is_dead { true } else { forward_blk(tg_opt) } + }; + /*##########################################################* + * Step 2: Recurse on the rest of the list; maybe coalescing. + *##########################################################*/ + // 'need_unwind' is only set if blk returned true above, *and* + // the recursive call early-broke. + let mut need_unwind = false; + if do_continue { + // NB: Takes many locks! (ancestor nodes & parent groups) + need_unwind = coalesce(&mut nobe.ancestors, bail_opt, + forward_blk, nobe.generation); + } + /*##########################################################* + * Step 3: Maybe unwind; compute return info for our caller. + *##########################################################*/ + if need_unwind && !nobe_is_dead { + for bail_opt.each |bail_blk| { + do with_parent_tg(&mut nobe.parent_group) |tg_opt| { + (*bail_blk)(tg_opt) + } + } + } + // Decide whether our caller should unwind. + need_unwind = need_unwind || !do_continue; + // Tell caller whether or not to coalesce and/or unwind + if nobe_is_dead { + // Swap the list out here; the caller replaces us with it. + let rest = util::replace(&mut nobe.ancestors, + AncestorList(None)); + (Some(rest), need_unwind) + } else { + (None, need_unwind) + } + } + }; + + // Wrapper around exclusive::with that appeases borrowck. + fn with_parent_tg<U>(parent_group: &mut Option<TaskGroupArc>, + blk: &fn(TaskGroupInner) -> U) -> U { + // If this trips, more likely the problem is 'blk' failed inside. + let tmp_arc = parent_group.swap_unwrap(); + let result = do access_group(&tmp_arc) |tg_opt| { blk(tg_opt) }; + *parent_group = Some(tmp_arc); + result + } + } +} + +// One of these per task. +struct TCB { + me: *rust_task, + // List of tasks with whose fates this one's is intertwined. + tasks: TaskGroupArc, // 'none' means the group has failed. + // Lists of tasks who will kill us if they fail, but whom we won't kill. + ancestors: AncestorList, + is_main: bool, + notifier: Option<AutoNotify>, +} + +impl Drop for TCB { + // Runs on task exit. + fn finalize(&self) { + unsafe { + let this: &mut TCB = transmute(self); + + // If we are failing, the whole taskgroup needs to die. + if rt::rust_task_is_unwinding(self.me) { + for this.notifier.each_mut |x| { + x.failed = true; + } + // Take everybody down with us. + do access_group(&self.tasks) |tg| { + kill_taskgroup(tg, self.me, self.is_main); + } + } else { + // Remove ourselves from the group(s). + do access_group(&self.tasks) |tg| { + leave_taskgroup(tg, self.me, true); + } + } + // It doesn't matter whether this happens before or after dealing + // with our own taskgroup, so long as both happen before we die. + // We remove ourself from every ancestor we can, so no cleanup; no + // break. + for each_ancestor(&mut this.ancestors, None) |ancestor_group| { + leave_taskgroup(ancestor_group, self.me, false); + }; + } + } +} + +fn TCB(me: *rust_task, + tasks: TaskGroupArc, + ancestors: AncestorList, + is_main: bool, + mut notifier: Option<AutoNotify>) -> TCB { + for notifier.each_mut |x| { + x.failed = false; + } + + TCB { + me: me, + tasks: tasks, + ancestors: ancestors, + is_main: is_main, + notifier: notifier + } +} + +struct AutoNotify { + notify_chan: Chan<TaskResult>, + failed: bool, +} + +impl Drop for AutoNotify { + fn finalize(&self) { + let result = if self.failed { Failure } else { Success }; + self.notify_chan.send(result); + } +} + +fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify { + AutoNotify { + notify_chan: chan, + failed: true // Un-set above when taskgroup successfully made. + } +} + +fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task, + is_member: bool) -> bool { + let newstate = util::replace(&mut *state, None); + // If 'None', the group was failing. Can't enlist. + if newstate.is_some() { + let mut group = newstate.unwrap(); + taskset_insert(if is_member { + &mut group.members + } else { + &mut group.descendants + }, me); + *state = Some(group); + true + } else { + false + } +} + +// NB: Runs in destructor/post-exit context. Can't 'fail'. +fn leave_taskgroup(state: TaskGroupInner, me: *rust_task, + is_member: bool) { + let newstate = util::replace(&mut *state, None); + // If 'None', already failing and we've already gotten a kill signal. + if newstate.is_some() { + let mut group = newstate.unwrap(); + taskset_remove(if is_member { + &mut group.members + } else { + &mut group.descendants + }, me); + *state = Some(group); + } +} + +// NB: Runs in destructor/post-exit context. Can't 'fail'. +fn kill_taskgroup(state: TaskGroupInner, me: *rust_task, is_main: bool) { + unsafe { + // NB: We could do the killing iteration outside of the group arc, by + // having "let mut newstate" here, swapping inside, and iterating + // after. But that would let other exiting tasks fall-through and exit + // while we were trying to kill them, causing potential + // use-after-free. A task's presence in the arc guarantees it's alive + // only while we hold the lock, so if we're failing, all concurrently + // exiting tasks must wait for us. To do it differently, we'd have to + // use the runtime's task refcounting, but that could leave task + // structs around long after their task exited. + let newstate = util::replace(state, None); + // Might already be None, if Somebody is failing simultaneously. + // That's ok; only one task needs to do the dirty work. (Might also + // see 'None' if Somebody already failed and we got a kill signal.) + if newstate.is_some() { + let group = newstate.unwrap(); + for taskset_each(&group.members) |sibling| { + // Skip self - killing ourself won't do much good. + if sibling != me { + rt::rust_task_kill_other(sibling); + } + } + for taskset_each(&group.descendants) |child| { + assert!(child != me); + rt::rust_task_kill_other(child); + } + // Only one task should ever do this. + if is_main { + rt::rust_task_kill_all(me); + } + // Do NOT restore state to Some(..)! It stays None to indicate + // that the whole taskgroup is failing, to forbid new spawns. + } + // (note: multiple tasks may reach this point) + } +} + +// FIXME (#2912): Work around core-vs-coretest function duplication. Can't use +// a proper closure because the #[test]s won't understand. Have to fake it. +macro_rules! taskgroup_key ( + // Use a "code pointer" value that will never be a real code pointer. + () => (cast::transmute((-2 as uint, 0u))) +) + +fn gen_child_taskgroup(linked: bool, supervised: bool) + -> (TaskGroupArc, AncestorList, bool) { + unsafe { + let spawner = rt::rust_get_task(); + /*##################################################################* + * Step 1. Get spawner's taskgroup info. + *##################################################################*/ + let spawner_group: @@mut TCB = + match local_get(OldHandle(spawner), taskgroup_key!()) { + None => { + // Main task, doing first spawn ever. Lazily initialise + // here. + let mut members = new_taskset(); + taskset_insert(&mut members, spawner); + let tasks = exclusive(Some(TaskGroupData { + members: members, + descendants: new_taskset(), + })); + // Main task/group has no ancestors, no notifier, etc. + let group = @@mut TCB(spawner, + tasks, + AncestorList(None), + true, + None); + local_set(OldHandle(spawner), taskgroup_key!(), group); + group + } + Some(group) => group + }; + let spawner_group: &mut TCB = *spawner_group; + + /*##################################################################* + * Step 2. Process spawn options for child. + *##################################################################*/ + return if linked { + // Child is in the same group as spawner. + let g = spawner_group.tasks.clone(); + // Child's ancestors are spawner's ancestors. + let a = share_ancestors(&mut spawner_group.ancestors); + // Propagate main-ness. + (g, a, spawner_group.is_main) + } else { + // Child is in a separate group from spawner. + let g = exclusive(Some(TaskGroupData { + members: new_taskset(), + descendants: new_taskset(), + })); + let a = if supervised { + // Child's ancestors start with the spawner. + let old_ancestors = + share_ancestors(&mut spawner_group.ancestors); + // FIXME(#3068) - The generation counter is only used for a + // debug assertion, but initialising it requires locking a + // mutex. Hence it should be enabled only in debug builds. + let new_generation = + match *old_ancestors { + Some(ref arc) => { + access_ancestors(arc, |a| a.generation+1) + } + None => 0 // the actual value doesn't really matter. + }; + assert!(new_generation < uint::max_value); + // Build a new node in the ancestor list. + AncestorList(Some(exclusive(AncestorNode { + generation: new_generation, + parent_group: Some(spawner_group.tasks.clone()), + ancestors: old_ancestors, + }))) + } else { + // Child has no ancestors. + AncestorList(None) + }; + (g, a, false) + }; + } + + fn share_ancestors(ancestors: &mut AncestorList) -> AncestorList { + // Appease the borrow-checker. Really this wants to be written as: + // match ancestors + // Some(ancestor_arc) { ancestor_list(Some(ancestor_arc.clone())) } + // None { ancestor_list(None) } + let tmp = util::replace(&mut **ancestors, None); + if tmp.is_some() { + let ancestor_arc = tmp.unwrap(); + let result = ancestor_arc.clone(); + **ancestors = Some(ancestor_arc); + AncestorList(Some(result)) + } else { + AncestorList(None) + } + } +} + +pub fn spawn_raw(opts: TaskOpts, f: ~fn()) { + use rt::*; + + match context() { + OldTaskContext => { + spawn_raw_oldsched(opts, f) + } + TaskContext => { + spawn_raw_newsched(opts, f) + } + SchedulerContext => { + fail!("can't spawn from scheduler context") + } + GlobalContext => { + fail!("can't spawn from global context") + } + } +} + +fn spawn_raw_newsched(_opts: TaskOpts, f: ~fn()) { + use rt::sched::*; + + let mut sched = Local::take::<Scheduler>(); + let task = ~Coroutine::new(&mut sched.stack_pool, f); + sched.schedule_new_task(task); +} + +fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) { + + let (child_tg, ancestors, is_main) = + gen_child_taskgroup(opts.linked, opts.supervised); + + unsafe { + let child_data = Cell((child_tg, ancestors, f)); + // Being killed with the unsafe task/closure pointers would leak them. + do unkillable { + // Agh. Get move-mode items into the closure. FIXME (#2829) + let (child_tg, ancestors, f) = child_data.take(); + // Create child task. + let new_task = match opts.sched.mode { + DefaultScheduler => rt::new_task(), + _ => new_task_in_sched(opts.sched) + }; + assert!(!new_task.is_null()); + // Getting killed after here would leak the task. + let notify_chan = if opts.notify_chan.is_none() { + None + } else { + Some(opts.notify_chan.swap_unwrap()) + }; + + let child_wrapper = make_child_wrapper(new_task, child_tg, + ancestors, is_main, notify_chan, f); + + let closure = cast::transmute(&child_wrapper); + + // Getting killed between these two calls would free the child's + // closure. (Reordering them wouldn't help - then getting killed + // between them would leak.) + rt::start_task(new_task, closure); + cast::forget(child_wrapper); + } + } + + // This function returns a closure-wrapper that we pass to the child task. + // (1) It sets up the notification channel. + // (2) It attempts to enlist in the child's group and all ancestor groups. + // (3a) If any of those fails, it leaves all groups, and does nothing. + // (3b) Otherwise it builds a task control structure and puts it in TLS, + // (4) ...and runs the provided body function. + fn make_child_wrapper(child: *rust_task, child_arc: TaskGroupArc, + ancestors: AncestorList, is_main: bool, + notify_chan: Option<Chan<TaskResult>>, + f: ~fn()) + -> ~fn() { + let child_data = Cell((child_arc, ancestors)); + let result: ~fn() = || { + // Agh. Get move-mode items into the closure. FIXME (#2829) + let mut (child_arc, ancestors) = child_data.take(); + // Child task runs this code. + + // Even if the below code fails to kick the child off, we must + // send Something on the notify channel. + + //let mut notifier = None;//notify_chan.map(|c| AutoNotify(c)); + let notifier = match notify_chan { + Some(ref notify_chan_value) => { + let moved_ncv = move_it!(*notify_chan_value); + Some(AutoNotify(moved_ncv)) + } + _ => None + }; + + if enlist_many(child, &child_arc, &mut ancestors) { + let group = @@mut TCB(child, + child_arc, + ancestors, + is_main, + notifier); + unsafe { + local_set(OldHandle(child), taskgroup_key!(), group); + } + + // Run the child's body. + f(); + + // TLS cleanup code will exit the taskgroup. + } + + // Run the box annihilator. + // FIXME #4428: Crashy. + // unsafe { cleanup::annihilate(); } + }; + return result; + + // Set up membership in taskgroup and descendantship in all ancestor + // groups. If any enlistment fails, Some task was already failing, so + // don't let the child task run, and undo every successful enlistment. + fn enlist_many(child: *rust_task, child_arc: &TaskGroupArc, + ancestors: &mut AncestorList) -> bool { + // Join this taskgroup. + let mut result = + do access_group(child_arc) |child_tg| { + enlist_in_taskgroup(child_tg, child, true) // member + }; + if result { + // Unwinding function in case any ancestral enlisting fails + let bail: @fn(TaskGroupInner) = |tg| { + leave_taskgroup(tg, child, false) + }; + // Attempt to join every ancestor group. + result = + each_ancestor(ancestors, Some(bail), |ancestor_tg| { + // Enlist as a descendant, not as an actual member. + // Descendants don't kill ancestor groups on failure. + enlist_in_taskgroup(ancestor_tg, child, false) + }); + // If any ancestor group fails, need to exit this group too. + if !result { + do access_group(child_arc) |child_tg| { + leave_taskgroup(child_tg, child, true); // member + } + } + } + result + } + } + + fn new_task_in_sched(opts: SchedOpts) -> *rust_task { + if opts.foreign_stack_size != None { + fail!("foreign_stack_size scheduler option unimplemented"); + } + + let num_threads = match opts.mode { + DefaultScheduler + | CurrentScheduler + | ExistingScheduler(*) + | PlatformThread => 0u, /* Won't be used */ + SingleThreaded => 1u, + ThreadPerCore => unsafe { rt::rust_num_threads() }, + ThreadPerTask => { + fail!("ThreadPerTask scheduling mode unimplemented") + } + ManualThreads(threads) => { + if threads == 0u { + fail!("can not create a scheduler with no threads"); + } + threads + } + }; + + unsafe { + let sched_id = match opts.mode { + CurrentScheduler => rt::rust_get_sched_id(), + ExistingScheduler(SchedulerHandle(id)) => id, + PlatformThread => rt::rust_osmain_sched_id(), + _ => rt::rust_new_sched(num_threads) + }; + rt::rust_new_task_in_sched(sched_id) + } + } +} + +#[test] +fn test_spawn_raw_simple() { + let (po, ch) = stream(); + do spawn_raw(default_task_opts()) { + ch.send(()); + } + po.recv(); +} + +#[test] +#[ignore(cfg(windows))] +fn test_spawn_raw_unsupervise() { + let opts = task::TaskOpts { + linked: false, + notify_chan: None, + .. default_task_opts() + }; + do spawn_raw(opts) { + fail!(); + } +} + +#[test] +#[ignore(cfg(windows))] +fn test_spawn_raw_notify_success() { + let (notify_po, notify_ch) = comm::stream(); + + let opts = task::TaskOpts { + notify_chan: Some(notify_ch), + .. default_task_opts() + }; + do spawn_raw(opts) { + } + assert_eq!(notify_po.recv(), Success); +} + +#[test] +#[ignore(cfg(windows))] +fn test_spawn_raw_notify_failure() { + // New bindings for these + let (notify_po, notify_ch) = comm::stream(); + + let opts = task::TaskOpts { + linked: false, + notify_chan: Some(notify_ch), + .. default_task_opts() + }; + do spawn_raw(opts) { + fail!(); + } + assert_eq!(notify_po.recv(), Failure); +} diff --git a/src/libstd/task_pool.rs b/src/libstd/task_pool.rs deleted file mode 100644 index 0c52e1ff80e..00000000000 --- a/src/libstd/task_pool.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/// A task pool abstraction. Useful for achieving predictable CPU -/// parallelism. - -use core::comm::Chan; -use core::task::SchedMode; -use core::task; -use core::vec; - -#[cfg(test)] use core::task::SingleThreaded; - -enum Msg<T> { - Execute(~fn(&T)), - Quit -} - -pub struct TaskPool<T> { - channels: ~[Chan<Msg<T>>], - next_index: uint, -} - -#[unsafe_destructor] -impl<T> Drop for TaskPool<T> { - fn finalize(&self) { - for self.channels.each |channel| { - channel.send(Quit); - } - } -} - -pub impl<T> TaskPool<T> { - /// Spawns a new task pool with `n_tasks` tasks. If the `sched_mode` - /// is None, the tasks run on this scheduler; otherwise, they run on a - /// new scheduler with the given mode. The provided `init_fn_factory` - /// returns a function which, given the index of the task, should return - /// local data to be kept around in that task. - fn new(n_tasks: uint, - opt_sched_mode: Option<SchedMode>, - init_fn_factory: ~fn() -> ~fn(uint) -> T) - -> TaskPool<T> { - assert!(n_tasks >= 1); - - let channels = do vec::from_fn(n_tasks) |i| { - let (port, chan) = comm::stream::<Msg<T>>(); - let init_fn = init_fn_factory(); - - let task_body: ~fn() = || { - let local_data = init_fn(i); - loop { - match port.recv() { - Execute(f) => f(&local_data), - Quit => break - } - } - }; - - // Start the task. - match opt_sched_mode { - None => { - // Run on this scheduler. - task::spawn(task_body); - } - Some(sched_mode) => { - let mut task = task::task(); - task.sched_mode(sched_mode); - task.spawn(task_body); - } - } - - chan - }; - - return TaskPool { channels: channels, next_index: 0 }; - } - - /// Executes the function `f` on a task in the pool. The function - /// receives a reference to the local data returned by the `init_fn`. - fn execute(&mut self, f: ~fn(&T)) { - self.channels[self.next_index].send(Execute(f)); - self.next_index += 1; - if self.next_index == self.channels.len() { self.next_index = 0; } - } -} - -#[test] -fn test_task_pool() { - let f: ~fn() -> ~fn(uint) -> uint = || { - let g: ~fn(uint) -> uint = |i| i; - g - }; - let mut pool = TaskPool::new(4, Some(SingleThreaded), f); - for 8.times { - pool.execute(|i| io::println(fmt!("Hello from thread %u!", *i))); - } -} diff --git a/src/libstd/tempfile.rs b/src/libstd/tempfile.rs deleted file mode 100644 index e02a7a33733..00000000000 --- a/src/libstd/tempfile.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Temporary files and directories - -use core::rand::RngUtil; - -pub fn mkdtemp(tmpdir: &Path, suffix: &str) -> Option<Path> { - let mut r = rand::rng(); - for 1000.times { - let p = tmpdir.push(r.gen_str(16) + suffix); - if os::make_dir(&p, 0x1c0) { // 700 - return Some(p); - } - } - None -} - -#[cfg(test)] -mod tests { - use tempfile::mkdtemp; - use tempfile; - use core::os; - - #[test] - fn test_mkdtemp() { - let p = mkdtemp(&Path("."), "foobar").unwrap(); - os::remove_dir(&p); - assert!(str::ends_with(p.to_str(), "foobar")); - } - - // Ideally these would be in core::os but then core would need - // to depend on std - #[test] - fn recursive_mkdir_rel() { - use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; - use core::os; - - let root = mkdtemp(&os::tmpdir(), "recursive_mkdir_rel"). - expect("recursive_mkdir_rel"); - assert!(do os::change_dir_locked(&root) { - let path = Path("frob"); - debug!("recursive_mkdir_rel: Making: %s in cwd %s [%?]", path.to_str(), - os::getcwd().to_str(), - os::path_exists(&path)); - assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - assert!(os::path_is_dir(&path)); - assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - assert!(os::path_is_dir(&path)); - }); - } - - #[test] - fn recursive_mkdir_dot() { - use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; - use core::os; - - let dot = Path("."); - assert!(os::mkdir_recursive(&dot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - let dotdot = Path(".."); - assert!(os::mkdir_recursive(&dotdot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - } - - #[test] - fn recursive_mkdir_rel_2() { - use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; - use core::os; - - let root = mkdtemp(&os::tmpdir(), "recursive_mkdir_rel_2"). - expect("recursive_mkdir_rel_2"); - assert!(do os::change_dir_locked(&root) { - let path = Path("./frob/baz"); - debug!("recursive_mkdir_rel_2: Making: %s in cwd %s [%?]", path.to_str(), - os::getcwd().to_str(), os::path_exists(&path)); - assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - assert!(os::path_is_dir(&path)); - assert!(os::path_is_dir(&path.pop())); - let path2 = Path("quux/blat"); - debug!("recursive_mkdir_rel_2: Making: %s in cwd %s", path2.to_str(), - os::getcwd().to_str()); - assert!(os::mkdir_recursive(&path2, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - assert!(os::path_is_dir(&path2)); - assert!(os::path_is_dir(&path2.pop())); - }); - } - - // Ideally this would be in core, but needs mkdtemp - #[test] - pub fn test_rmdir_recursive_ok() { - use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; - use core::os; - - let rwx = (S_IRUSR | S_IWUSR | S_IXUSR) as i32; - - let tmpdir = mkdtemp(&os::tmpdir(), "test").expect("test_rmdir_recursive_ok: \ - couldn't create temp dir"); - let root = tmpdir.push("foo"); - - debug!("making %s", root.to_str()); - assert!(os::make_dir(&root, rwx)); - assert!(os::make_dir(&root.push("foo"), rwx)); - assert!(os::make_dir(&root.push("foo").push("bar"), rwx)); - assert!(os::make_dir(&root.push("foo").push("bar").push("blat"), rwx)); - assert!(os::remove_dir_recursive(&root)); - assert!(!os::path_exists(&root)); - assert!(!os::path_exists(&root.push("bar"))); - assert!(!os::path_exists(&root.push("bar").push("blat"))); - } -} diff --git a/src/libstd/term.rs b/src/libstd/term.rs deleted file mode 100644 index fcac7062210..00000000000 --- a/src/libstd/term.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Simple ANSI color library - -use core::io; -use core::option; -use core::os; - -// FIXME (#2807): Windows support. - -pub static color_black: u8 = 0u8; -pub static color_red: u8 = 1u8; -pub static color_green: u8 = 2u8; -pub static color_yellow: u8 = 3u8; -pub static color_blue: u8 = 4u8; -pub static color_magenta: u8 = 5u8; -pub static color_cyan: u8 = 6u8; -pub static color_light_gray: u8 = 7u8; -pub static color_light_grey: u8 = 7u8; -pub static color_dark_gray: u8 = 8u8; -pub static color_dark_grey: u8 = 8u8; -pub static color_bright_red: u8 = 9u8; -pub static color_bright_green: u8 = 10u8; -pub static color_bright_yellow: u8 = 11u8; -pub static color_bright_blue: u8 = 12u8; -pub static color_bright_magenta: u8 = 13u8; -pub static color_bright_cyan: u8 = 14u8; -pub static color_bright_white: u8 = 15u8; - -pub fn esc(writer: @io::Writer) { writer.write([0x1bu8, '[' as u8]); } - -/// Reset the foreground and background colors to default -pub fn reset(writer: @io::Writer) { - esc(writer); - writer.write(['0' as u8, 'm' as u8]); -} - -/// Returns true if the terminal supports color -pub fn color_supported() -> bool { - let supported_terms = ~[~"xterm-color", ~"xterm", - ~"screen-bce", ~"xterm-256color"]; - return match os::getenv("TERM") { - option::Some(ref env) => { - for supported_terms.each |term| { - if *term == *env { return true; } - } - false - } - option::None => false - }; -} - -pub fn set_color(writer: @io::Writer, first_char: u8, color: u8) { - assert!((color < 16u8)); - esc(writer); - let mut color = color; - if color >= 8u8 { writer.write(['1' as u8, ';' as u8]); color -= 8u8; } - writer.write([first_char, ('0' as u8) + color, 'm' as u8]); -} - -/// Set the foreground color -pub fn fg(writer: @io::Writer, color: u8) { - return set_color(writer, '3' as u8, color); -} - -/// Set the background color -pub fn bg(writer: @io::Writer, color: u8) { - return set_color(writer, '4' as u8, color); -} diff --git a/src/libstd/test.rs b/src/libstd/test.rs deleted file mode 100644 index 4046ce83c9d..00000000000 --- a/src/libstd/test.rs +++ /dev/null @@ -1,966 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#[doc(hidden)]; - -// Support code for rustc's built in test runner generator. Currently, -// none of this is meant for users. It is intended to support the -// simplest interface possible for representing and running tests -// while providing a base that other test frameworks may build off of. - -use getopts; -use sort; -use term; - -use core::to_str::ToStr; -use core::comm::{stream, SharedChan}; - -pub mod rustrt { - use core::libc::size_t; - - #[abi = "cdecl"] - pub extern { - pub unsafe fn rust_sched_threads() -> size_t; - } -} - -// The name of a test. By convention this follows the rules for rust -// paths; i.e. it should be a series of identifiers separated by double -// colons. This way if some test runner wants to arrange the tests -// hierarchically it may. - -pub enum TestName { - StaticTestName(&'static str), - DynTestName(~str) -} -impl ToStr for TestName { - fn to_str(&self) -> ~str { - match copy *self { - StaticTestName(s) => s.to_str(), - DynTestName(s) => s.to_str() - } - } -} - -// A function that runs a test. If the function returns successfully, -// the test succeeds; if the function fails then the test fails. We -// may need to come up with a more clever definition of test in order -// to support isolation of tests into tasks. -pub enum TestFn { - StaticTestFn(extern fn()), - StaticBenchFn(extern fn(&mut BenchHarness)), - DynTestFn(~fn()), - DynBenchFn(~fn(&mut BenchHarness)) -} - -// Structure passed to BenchFns -pub struct BenchHarness { - iterations: u64, - ns_start: u64, - ns_end: u64, - bytes: u64 -} - -// The definition of a single test. A test runner will run a list of -// these. -pub struct TestDesc { - name: TestName, - ignore: bool, - should_fail: bool -} - -pub struct TestDescAndFn { - desc: TestDesc, - testfn: TestFn, -} - -// The default console test runner. It accepts the command line -// arguments and a vector of test_descs. -pub fn test_main(args: &[~str], tests: ~[TestDescAndFn]) { - let opts = - match parse_opts(args) { - either::Left(o) => o, - either::Right(m) => fail!(m) - }; - if !run_tests_console(&opts, tests) { fail!("Some tests failed"); } -} - -// A variant optimized for invocation with a static test vector. -// This will fail (intentionally) when fed any dynamic tests, because -// it is copying the static values out into a dynamic vector and cannot -// copy dynamic values. It is doing this because from this point on -// a ~[TestDescAndFn] is used in order to effect ownership-transfer -// semantics into parallel test runners, which in turn requires a ~[] -// rather than a &[]. -pub fn test_main_static(args: &[~str], tests: &[TestDescAndFn]) { - let owned_tests = do tests.map |t| { - match t.testfn { - StaticTestFn(f) => - TestDescAndFn { testfn: StaticTestFn(f), desc: copy t.desc }, - - StaticBenchFn(f) => - TestDescAndFn { testfn: StaticBenchFn(f), desc: copy t.desc }, - - _ => { - fail!("non-static tests passed to test::test_main_static"); - } - } - }; - test_main(args, owned_tests) -} - -pub struct TestOpts { - filter: Option<~str>, - run_ignored: bool, - run_tests: bool, - run_benchmarks: bool, - save_results: Option<Path>, - compare_results: Option<Path>, - logfile: Option<Path> -} - -type OptRes = Either<TestOpts, ~str>; - -// Parses command line arguments into test options -pub fn parse_opts(args: &[~str]) -> OptRes { - let args_ = vec::tail(args); - let opts = ~[getopts::optflag("ignored"), - getopts::optflag("test"), - getopts::optflag("bench"), - getopts::optopt("save"), - getopts::optopt("diff"), - getopts::optopt("logfile")]; - let matches = - match getopts::getopts(args_, opts) { - Ok(m) => m, - Err(f) => return either::Right(getopts::fail_str(f)) - }; - - let filter = - if matches.free.len() > 0 { - option::Some(copy (matches).free[0]) - } else { option::None }; - - let run_ignored = getopts::opt_present(&matches, "ignored"); - - let logfile = getopts::opt_maybe_str(&matches, "logfile"); - let logfile = logfile.map(|s| Path(*s)); - - let run_benchmarks = getopts::opt_present(&matches, "bench"); - let run_tests = ! run_benchmarks || - getopts::opt_present(&matches, "test"); - - let save_results = getopts::opt_maybe_str(&matches, "save"); - let save_results = save_results.map(|s| Path(*s)); - - let compare_results = getopts::opt_maybe_str(&matches, "diff"); - let compare_results = compare_results.map(|s| Path(*s)); - - let test_opts = TestOpts { - filter: filter, - run_ignored: run_ignored, - run_tests: run_tests, - run_benchmarks: run_benchmarks, - save_results: save_results, - compare_results: compare_results, - logfile: logfile - }; - - either::Left(test_opts) -} - -#[deriving(Eq)] -pub struct BenchSamples { - ns_iter_samples: ~[f64], - mb_s: uint -} - -#[deriving(Eq)] -pub enum TestResult { TrOk, TrFailed, TrIgnored, TrBench(BenchSamples) } - -struct ConsoleTestState { - out: @io::Writer, - log_out: Option<@io::Writer>, - use_color: bool, - total: uint, - passed: uint, - failed: uint, - ignored: uint, - benchmarked: uint, - failures: ~[TestDesc] -} - -// A simple console test runner -pub fn run_tests_console(opts: &TestOpts, - tests: ~[TestDescAndFn]) -> bool { - - fn callback(event: &TestEvent, st: &mut ConsoleTestState) { - debug!("callback(event=%?)", event); - match copy *event { - TeFiltered(ref filtered_tests) => { - st.total = filtered_tests.len(); - let noun = if st.total != 1 { ~"tests" } else { ~"test" }; - st.out.write_line(fmt!("\nrunning %u %s", st.total, noun)); - } - TeWait(ref test) => st.out.write_str( - fmt!("test %s ... ", test.name.to_str())), - TeResult(copy test, result) => { - match st.log_out { - Some(f) => write_log(f, copy result, &test), - None => () - } - match result { - TrOk => { - st.passed += 1; - write_ok(st.out, st.use_color); - st.out.write_line(""); - } - TrFailed => { - st.failed += 1; - write_failed(st.out, st.use_color); - st.out.write_line(""); - st.failures.push(test); - } - TrIgnored => { - st.ignored += 1; - write_ignored(st.out, st.use_color); - st.out.write_line(""); - } - TrBench(bs) => { - st.benchmarked += 1u; - write_bench(st.out, st.use_color); - st.out.write_line(fmt!(": %s", - fmt_bench_samples(&bs))); - } - } - } - } - } - - let log_out = match opts.logfile { - Some(ref path) => match io::file_writer(path, - [io::Create, - io::Truncate]) { - result::Ok(w) => Some(w), - result::Err(ref s) => { - fail!("can't open output file: %s", *s) - } - }, - None => None - }; - - let st = @mut ConsoleTestState { - out: io::stdout(), - log_out: log_out, - use_color: use_color(), - total: 0u, - passed: 0u, - failed: 0u, - ignored: 0u, - benchmarked: 0u, - failures: ~[] - }; - - run_tests(opts, tests, |x| callback(&x, st)); - - assert!(st.passed + st.failed + - st.ignored + st.benchmarked == st.total); - let success = st.failed == 0u; - - if !success { - print_failures(st); - } - - { - let st: &mut ConsoleTestState = st; - st.out.write_str(fmt!("\nresult: ")); - if success { - // There's no parallelism at this point so it's safe to use color - write_ok(st.out, true); - } else { - write_failed(st.out, true); - } - st.out.write_str(fmt!(". %u passed; %u failed; %u ignored\n\n", - st.passed, st.failed, st.ignored)); - } - - return success; - - fn fmt_bench_samples(bs: &BenchSamples) -> ~str { - use stats::Stats; - if bs.mb_s != 0 { - fmt!("%u ns/iter (+/- %u) = %u MB/s", - bs.ns_iter_samples.median() as uint, - 3 * (bs.ns_iter_samples.median_abs_dev() as uint), - bs.mb_s) - } else { - fmt!("%u ns/iter (+/- %u)", - bs.ns_iter_samples.median() as uint, - 3 * (bs.ns_iter_samples.median_abs_dev() as uint)) - } - } - - fn write_log(out: @io::Writer, result: TestResult, test: &TestDesc) { - out.write_line(fmt!("%s %s", - match result { - TrOk => ~"ok", - TrFailed => ~"failed", - TrIgnored => ~"ignored", - TrBench(ref bs) => fmt_bench_samples(bs) - }, test.name.to_str())); - } - - fn write_ok(out: @io::Writer, use_color: bool) { - write_pretty(out, "ok", term::color_green, use_color); - } - - fn write_failed(out: @io::Writer, use_color: bool) { - write_pretty(out, "FAILED", term::color_red, use_color); - } - - fn write_ignored(out: @io::Writer, use_color: bool) { - write_pretty(out, "ignored", term::color_yellow, use_color); - } - - fn write_bench(out: @io::Writer, use_color: bool) { - write_pretty(out, "bench", term::color_cyan, use_color); - } - - fn write_pretty(out: @io::Writer, - word: &str, - color: u8, - use_color: bool) { - if use_color && term::color_supported() { - term::fg(out, color); - } - out.write_str(word); - if use_color && term::color_supported() { - term::reset(out); - } - } -} - -fn print_failures(st: &ConsoleTestState) { - st.out.write_line("\nfailures:"); - let mut failures = ~[]; - for uint::range(0, vec::uniq_len(&const st.failures)) |i| { - let name = copy st.failures[i].name; - failures.push(name.to_str()); - } - sort::tim_sort(failures); - for failures.each |name| { - st.out.write_line(fmt!(" %s", name.to_str())); - } -} - -#[test] -fn should_sort_failures_before_printing_them() { - fn dummy() {} - - let s = do io::with_str_writer |wr| { - let test_a = TestDesc { - name: StaticTestName("a"), - ignore: false, - should_fail: false - }; - - let test_b = TestDesc { - name: StaticTestName("b"), - ignore: false, - should_fail: false - }; - - let st = @ConsoleTestState { - out: wr, - log_out: option::None, - use_color: false, - total: 0u, - passed: 0u, - failed: 0u, - ignored: 0u, - benchmarked: 0u, - failures: ~[test_b, test_a] - }; - - print_failures(st); - }; - - let apos = str::find_str(s, ~"a").get(); - let bpos = str::find_str(s, ~"b").get(); - assert!(apos < bpos); -} - -fn use_color() -> bool { return get_concurrency() == 1; } - -enum TestEvent { - TeFiltered(~[TestDesc]), - TeWait(TestDesc), - TeResult(TestDesc, TestResult), -} - -type MonitorMsg = (TestDesc, TestResult); - -fn run_tests(opts: &TestOpts, - tests: ~[TestDescAndFn], - callback: @fn(e: TestEvent)) { - - let filtered_tests = filter_tests(opts, tests); - let filtered_descs = filtered_tests.map(|t| copy t.desc); - - callback(TeFiltered(filtered_descs)); - - let (filtered_tests, filtered_benchs) = - do vec::partition(filtered_tests) |e| { - match e.testfn { - StaticTestFn(_) | DynTestFn(_) => true, - StaticBenchFn(_) | DynBenchFn(_) => false - } - }; - - // It's tempting to just spawn all the tests at once, but since we have - // many tests that run in other processes we would be making a big mess. - let concurrency = get_concurrency(); - debug!("using %u test tasks", concurrency); - - let mut remaining = filtered_tests; - vec::reverse(remaining); - let mut pending = 0; - - let (p, ch) = stream(); - let ch = SharedChan::new(ch); - - while pending > 0 || !remaining.is_empty() { - while pending < concurrency && !remaining.is_empty() { - let test = remaining.pop(); - if concurrency == 1 { - // We are doing one test at a time so we can print the name - // of the test before we run it. Useful for debugging tests - // that hang forever. - callback(TeWait(copy test.desc)); - } - run_test(!opts.run_tests, test, ch.clone()); - pending += 1; - } - - let (desc, result) = p.recv(); - if concurrency != 1 { - callback(TeWait(copy desc)); - } - callback(TeResult(desc, result)); - pending -= 1; - } - - // All benchmarks run at the end, in serial. - do vec::consume(filtered_benchs) |_, b| { - callback(TeWait(copy b.desc)); - run_test(!opts.run_benchmarks, b, ch.clone()); - let (test, result) = p.recv(); - callback(TeResult(test, result)); - } -} - -// Windows tends to dislike being overloaded with threads. -#[cfg(windows)] -static sched_overcommit : uint = 1; - -#[cfg(unix)] -static sched_overcommit : uint = 4u; - -fn get_concurrency() -> uint { - unsafe { - let threads = rustrt::rust_sched_threads() as uint; - if threads == 1 { 1 } - else { threads * sched_overcommit } - } -} - -#[allow(non_implicitly_copyable_typarams)] -pub fn filter_tests( - opts: &TestOpts, - tests: ~[TestDescAndFn]) -> ~[TestDescAndFn] -{ - let mut filtered = tests; - - // Remove tests that don't match the test filter - filtered = if opts.filter.is_none() { - filtered - } else { - let filter_str = - match opts.filter { - option::Some(copy f) => f, - option::None => ~"" - }; - - fn filter_fn(test: TestDescAndFn, filter_str: &str) -> - Option<TestDescAndFn> { - if str::contains(test.desc.name.to_str(), filter_str) { - return option::Some(test); - } else { return option::None; } - } - - vec::filter_map(filtered, |x| filter_fn(x, filter_str)) - }; - - // Maybe pull out the ignored test and unignore them - filtered = if !opts.run_ignored { - filtered - } else { - fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> { - if test.desc.ignore { - let TestDescAndFn {desc, testfn} = test; - Some(TestDescAndFn { - desc: TestDesc {ignore: false, ..desc}, - testfn: testfn - }) - } else { - None - } - }; - vec::filter_map(filtered, |x| filter(x)) - }; - - // Sort the tests alphabetically - fn lteq(t1: &TestDescAndFn, t2: &TestDescAndFn) -> bool { - str::le(t1.desc.name.to_str(), t2.desc.name.to_str()) - } - sort::quick_sort(filtered, lteq); - - filtered -} - -struct TestFuture { - test: TestDesc, - wait: @fn() -> TestResult, -} - -pub fn run_test(force_ignore: bool, - test: TestDescAndFn, - monitor_ch: SharedChan<MonitorMsg>) { - - let TestDescAndFn {desc, testfn} = test; - - if force_ignore || desc.ignore { - monitor_ch.send((desc, TrIgnored)); - return; - } - - fn run_test_inner(desc: TestDesc, - monitor_ch: SharedChan<MonitorMsg>, - testfn: ~fn()) { - let testfn_cell = ::core::cell::Cell(testfn); - do task::spawn { - let mut result_future = None; // task::future_result(builder); - - let mut task = task::task(); - task.unlinked(); - task.future_result(|r| { result_future = Some(r) }); - task.spawn(testfn_cell.take()); - - let task_result = result_future.unwrap().recv(); - let test_result = calc_result(&desc, - task_result == task::Success); - monitor_ch.send((copy desc, test_result)); - } - } - - match testfn { - DynBenchFn(benchfn) => { - let bs = ::test::bench::benchmark(benchfn); - monitor_ch.send((desc, TrBench(bs))); - return; - } - StaticBenchFn(benchfn) => { - let bs = ::test::bench::benchmark(benchfn); - monitor_ch.send((desc, TrBench(bs))); - return; - } - DynTestFn(f) => run_test_inner(desc, monitor_ch, f), - StaticTestFn(f) => run_test_inner(desc, monitor_ch, || f()) - } -} - -fn calc_result(desc: &TestDesc, task_succeeded: bool) -> TestResult { - if task_succeeded { - if desc.should_fail { TrFailed } - else { TrOk } - } else { - if desc.should_fail { TrOk } - else { TrFailed } - } -} - -pub mod bench { - use time::precise_time_ns; - use test::{BenchHarness, BenchSamples}; - use stats::Stats; - use core::rand::RngUtil; - - pub impl BenchHarness { - - /// Callback for benchmark functions to run in their body. - pub fn iter(&mut self, inner:&fn()) { - self.ns_start = precise_time_ns(); - let k = self.iterations; - for u64::range(0, k) |_| { - inner(); - } - self.ns_end = precise_time_ns(); - } - - fn ns_elapsed(&mut self) -> u64 { - if self.ns_start == 0 || self.ns_end == 0 { - 0 - } else { - self.ns_end - self.ns_start - } - } - - fn ns_per_iter(&mut self) -> u64 { - if self.iterations == 0 { - 0 - } else { - self.ns_elapsed() / self.iterations - } - } - - fn bench_n(&mut self, n: u64, f: &fn(&mut BenchHarness)) { - self.iterations = n; - debug!("running benchmark for %u iterations", - n as uint); - f(self); - } - - // This is the Go benchmark algorithm. It produces a single - // datapoint and always tries to run for 1s. - pub fn go_bench(&mut self, f: &fn(&mut BenchHarness)) { - - // Rounds a number down to the nearest power of 10. - fn round_down_10(n: u64) -> u64 { - let mut n = n; - let mut res = 1; - while n > 10 { - n = n / 10; - res *= 10; - } - res - } - - // Rounds x up to a number of the form [1eX, 2eX, 5eX]. - fn round_up(n: u64) -> u64 { - let base = round_down_10(n); - if n < (2 * base) { - 2 * base - } else if n < (5 * base) { - 5 * base - } else { - 10 * base - } - } - - // Initial bench run to get ballpark figure. - let mut n = 1_u64; - self.bench_n(n, f); - - while n < 1_000_000_000 && - self.ns_elapsed() < 1_000_000_000 { - let last = n; - - // Try to estimate iter count for 1s falling back to 1bn - // iterations if first run took < 1ns. - if self.ns_per_iter() == 0 { - n = 1_000_000_000; - } else { - n = 1_000_000_000 / self.ns_per_iter(); - } - - n = u64::max(u64::min(n+n/2, 100*last), last+1); - n = round_up(n); - self.bench_n(n, f); - } - } - - // This is a more statistics-driven benchmark algorithm. - // It stops as quickly as 50ms, so long as the statistical - // properties are satisfactory. If those properties are - // not met, it may run as long as the Go algorithm. - pub fn auto_bench(&mut self, f: &fn(&mut BenchHarness)) -> ~[f64] { - - let mut rng = rand::rng(); - let mut magnitude = 10; - let mut prev_madp = 0.0; - - loop { - let n_samples = rng.gen_uint_range(50, 60); - let n_iter = rng.gen_uint_range(magnitude, - magnitude * 2); - - let samples = do vec::from_fn(n_samples) |_| { - self.bench_n(n_iter as u64, f); - self.ns_per_iter() as f64 - }; - - // Eliminate outliers - let med = samples.median(); - let mad = samples.median_abs_dev(); - let samples = do vec::filter(samples) |f| { - num::abs(*f - med) <= 3.0 * mad - }; - - debug!("%u samples, median %f, MAD=%f, %u survived filter", - n_samples, med as float, mad as float, - samples.len()); - - if samples.len() != 0 { - // If we have _any_ cluster of signal... - let curr_madp = samples.median_abs_dev_pct(); - if self.ns_elapsed() > 1_000_000 && - (curr_madp < 1.0 || - num::abs(curr_madp - prev_madp) < 0.1) { - return samples; - } - prev_madp = curr_madp; - - if n_iter > 20_000_000 || - self.ns_elapsed() > 20_000_000 { - return samples; - } - } - - magnitude *= 2; - } - } - } - - pub fn benchmark(f: &fn(&mut BenchHarness)) -> BenchSamples { - - let mut bs = BenchHarness { - iterations: 0, - ns_start: 0, - ns_end: 0, - bytes: 0 - }; - - let ns_iter_samples = bs.auto_bench(f); - - let iter_s = 1_000_000_000 / (ns_iter_samples.median() as u64); - let mb_s = (bs.bytes * iter_s) / 1_000_000; - - BenchSamples { - ns_iter_samples: ns_iter_samples, - mb_s: mb_s as uint - } - } -} - -#[cfg(test)] -mod tests { - use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, - TestDesc, TestDescAndFn, - StaticTestName, DynTestName, DynTestFn}; - use test::{TestOpts, run_test}; - - use core::either; - use core::comm::{stream, SharedChan}; - use core::option; - use core::vec; - - #[test] - pub fn do_not_run_ignored_tests() { - fn f() { fail!(); } - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: true, - should_fail: false - }, - testfn: DynTestFn(|| f()), - }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); - run_test(false, desc, ch); - let (_, res) = p.recv(); - assert!(res != TrOk); - } - - #[test] - pub fn ignored_tests_result_in_ignored() { - fn f() { } - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: true, - should_fail: false - }, - testfn: DynTestFn(|| f()), - }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); - run_test(false, desc, ch); - let (_, res) = p.recv(); - assert_eq!(res, TrIgnored); - } - - #[test] - #[ignore(cfg(windows))] - fn test_should_fail() { - fn f() { fail!(); } - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: false, - should_fail: true - }, - testfn: DynTestFn(|| f()), - }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); - run_test(false, desc, ch); - let (_, res) = p.recv(); - assert_eq!(res, TrOk); - } - - #[test] - fn test_should_fail_but_succeeds() { - fn f() { } - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: false, - should_fail: true - }, - testfn: DynTestFn(|| f()), - }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); - run_test(false, desc, ch); - let (_, res) = p.recv(); - assert_eq!(res, TrFailed); - } - - #[test] - fn first_free_arg_should_be_a_filter() { - let args = ~[~"progname", ~"filter"]; - let opts = match parse_opts(args) { - either::Left(copy o) => o, - _ => fail!("Malformed arg in first_free_arg_should_be_a_filter") - }; - assert!("filter" == (copy opts.filter).get()); - } - - #[test] - fn parse_ignored_flag() { - let args = ~[~"progname", ~"filter", ~"--ignored"]; - let opts = match parse_opts(args) { - either::Left(copy o) => o, - _ => fail!("Malformed arg in parse_ignored_flag") - }; - assert!((opts.run_ignored)); - } - - #[test] - pub fn filter_for_ignored_option() { - fn dummy() {} - - // When we run ignored tests the test filter should filter out all the - // unignored tests and flip the ignore flag on the rest to false - - let opts = TestOpts { - filter: option::None, - run_ignored: true, - logfile: option::None, - run_tests: true, - run_benchmarks: false, - save_results: option::None, - compare_results: option::None - }; - - let tests = ~[ - TestDescAndFn { - desc: TestDesc { - name: StaticTestName("1"), - ignore: true, - should_fail: false, - }, - testfn: DynTestFn(|| {}), - }, - TestDescAndFn { - desc: TestDesc { - name: StaticTestName("2"), - ignore: false, - should_fail: false - }, - testfn: DynTestFn(|| {}), - }, - ]; - let filtered = filter_tests(&opts, tests); - - assert_eq!(filtered.len(), 1); - assert_eq!(filtered[0].desc.name.to_str(), ~"1"); - assert!(filtered[0].desc.ignore == false); - } - - #[test] - pub fn sort_tests() { - let opts = TestOpts { - filter: option::None, - run_ignored: false, - logfile: option::None, - run_tests: true, - run_benchmarks: false, - save_results: option::None, - compare_results: option::None - }; - - let names = - ~[~"sha1::test", ~"int::test_to_str", ~"int::test_pow", - ~"test::do_not_run_ignored_tests", - ~"test::ignored_tests_result_in_ignored", - ~"test::first_free_arg_should_be_a_filter", - ~"test::parse_ignored_flag", ~"test::filter_for_ignored_option", - ~"test::sort_tests"]; - let tests = - { - fn testfn() { } - let mut tests = ~[]; - for names.each |name| { - let test = TestDescAndFn { - desc: TestDesc { - name: DynTestName(copy *name), - ignore: false, - should_fail: false - }, - testfn: DynTestFn(copy testfn), - }; - tests.push(test); - } - tests - }; - let filtered = filter_tests(&opts, tests); - - let expected = - ~[~"int::test_pow", ~"int::test_to_str", ~"sha1::test", - ~"test::do_not_run_ignored_tests", - ~"test::filter_for_ignored_option", - ~"test::first_free_arg_should_be_a_filter", - ~"test::ignored_tests_result_in_ignored", - ~"test::parse_ignored_flag", - ~"test::sort_tests"]; - - let pairs = vec::zip(expected, filtered); - - for pairs.each |p| { - match *p { - (ref a, ref b) => { - assert!(*a == b.desc.name.to_str()); - } - } - } - } -} diff --git a/src/libstd/time.rs b/src/libstd/time.rs deleted file mode 100644 index 565ce2d0dd3..00000000000 --- a/src/libstd/time.rs +++ /dev/null @@ -1,1268 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -static NSEC_PER_SEC: i32 = 1_000_000_000_i32; - -pub mod rustrt { - use super::Tm; - - #[abi = "cdecl"] - pub extern { - pub unsafe fn get_time(sec: &mut i64, nsec: &mut i32); - - pub unsafe fn precise_time_ns(ns: &mut u64); - - pub unsafe fn rust_tzset(); - // FIXME: The i64 values can be passed by-val when #2064 is fixed. - pub unsafe fn rust_gmtime(sec: i64, nsec: i32, result: &mut Tm); - pub unsafe fn rust_localtime(sec: i64, nsec: i32, result: &mut Tm); - pub unsafe fn rust_timegm(tm: &Tm, sec: &mut i64); - pub unsafe fn rust_mktime(tm: &Tm, sec: &mut i64); - } -} - -/// A record specifying a time value in seconds and nanoseconds. -#[deriving(Eq, Encodable, Decodable)] -pub struct Timespec { sec: i64, nsec: i32 } - -/* - * Timespec assumes that pre-epoch Timespecs have negative sec and positive - * nsec fields. Darwin's and Linux's struct timespec functions handle pre- - * epoch timestamps using a "two steps back, one step forward" representation, - * though the man pages do not actually document this. For example, the time - * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64, - * nsec: 800_000_000_i32 }`. - */ -pub impl Timespec { - fn new(sec: i64, nsec: i32) -> Timespec { - assert!(nsec >= 0 && nsec < NSEC_PER_SEC); - Timespec { sec: sec, nsec: nsec } - } -} - -impl Ord for Timespec { - fn lt(&self, other: &Timespec) -> bool { - self.sec < other.sec || - (self.sec == other.sec && self.nsec < other.nsec) - } - fn le(&self, other: &Timespec) -> bool { !other.lt(self) } - fn ge(&self, other: &Timespec) -> bool { !self.lt(other) } - fn gt(&self, other: &Timespec) -> bool { !self.le(other) } -} - -/** - * Returns the current time as a `timespec` containing the seconds and - * nanoseconds since 1970-01-01T00:00:00Z. - */ -pub fn get_time() -> Timespec { - unsafe { - let mut sec = 0i64; - let mut nsec = 0i32; - rustrt::get_time(&mut sec, &mut nsec); - return Timespec::new(sec, nsec); - } -} - - -/** - * Returns the current value of a high-resolution performance counter - * in nanoseconds since an unspecified epoch. - */ -pub fn precise_time_ns() -> u64 { - unsafe { - let mut ns = 0u64; - rustrt::precise_time_ns(&mut ns); - ns - } -} - - -/** - * Returns the current value of a high-resolution performance counter - * in seconds since an unspecified epoch. - */ -pub fn precise_time_s() -> float { - return (precise_time_ns() as float) / 1000000000.; -} - -pub fn tzset() { - unsafe { - rustrt::rust_tzset(); - } -} - -#[deriving(Eq, Encodable, Decodable)] -pub struct Tm { - tm_sec: i32, // seconds after the minute ~[0-60] - tm_min: i32, // minutes after the hour ~[0-59] - tm_hour: i32, // hours after midnight ~[0-23] - tm_mday: i32, // days of the month ~[1-31] - tm_mon: i32, // months since January ~[0-11] - tm_year: i32, // years since 1900 - tm_wday: i32, // days since Sunday ~[0-6] - tm_yday: i32, // days since January 1 ~[0-365] - tm_isdst: i32, // Daylight Savings Time flag - tm_gmtoff: i32, // offset from UTC in seconds - tm_zone: ~str, // timezone abbreviation - tm_nsec: i32, // nanoseconds -} - -pub fn empty_tm() -> Tm { - Tm { - tm_sec: 0_i32, - tm_min: 0_i32, - tm_hour: 0_i32, - tm_mday: 0_i32, - tm_mon: 0_i32, - tm_year: 0_i32, - tm_wday: 0_i32, - tm_yday: 0_i32, - tm_isdst: 0_i32, - tm_gmtoff: 0_i32, - tm_zone: ~"", - tm_nsec: 0_i32, - } -} - -/// Returns the specified time in UTC -pub fn at_utc(clock: Timespec) -> Tm { - unsafe { - let Timespec { sec, nsec } = clock; - let mut tm = empty_tm(); - rustrt::rust_gmtime(sec, nsec, &mut tm); - tm - } -} - -/// Returns the current time in UTC -pub fn now_utc() -> Tm { - at_utc(get_time()) -} - -/// Returns the specified time in the local timezone -pub fn at(clock: Timespec) -> Tm { - unsafe { - let Timespec { sec, nsec } = clock; - let mut tm = empty_tm(); - rustrt::rust_localtime(sec, nsec, &mut tm); - tm - } -} - -/// Returns the current time in the local timezone -pub fn now() -> Tm { - at(get_time()) -} - -/// Parses the time from the string according to the format string. -pub fn strptime(s: &str, format: &str) -> Result<Tm, ~str> { - do_strptime(s, format) -} - -/// Formats the time according to the format string. -pub fn strftime(format: &str, tm: &Tm) -> ~str { - do_strftime(format, tm) -} - -pub impl Tm { - /// Convert time to the seconds from January 1, 1970 - fn to_timespec(&self) -> Timespec { - unsafe { - let mut sec = 0i64; - if self.tm_gmtoff == 0_i32 { - rustrt::rust_timegm(self, &mut sec); - } else { - rustrt::rust_mktime(self, &mut sec); - } - Timespec::new(sec, self.tm_nsec) - } - } - - /// Convert time to the local timezone - fn to_local(&self) -> Tm { - at(self.to_timespec()) - } - - /// Convert time to the UTC - fn to_utc(&self) -> Tm { - at_utc(self.to_timespec()) - } - - /** - * Return a string of the current time in the form - * "Thu Jan 1 00:00:00 1970". - */ - fn ctime(&self) -> ~str { self.strftime("%c") } - - /// Formats the time according to the format string. - fn strftime(&self, format: &str) -> ~str { - strftime(format, self) - } - - /** - * Returns a time string formatted according to RFC 822. - * - * local: "Thu, 22 Mar 2012 07:53:18 PST" - * utc: "Thu, 22 Mar 2012 14:53:18 UTC" - */ - fn rfc822(&self) -> ~str { - if self.tm_gmtoff == 0_i32 { - self.strftime("%a, %d %b %Y %T GMT") - } else { - self.strftime("%a, %d %b %Y %T %Z") - } - } - - /** - * Returns a time string formatted according to RFC 822 with Zulu time. - * - * local: "Thu, 22 Mar 2012 07:53:18 -0700" - * utc: "Thu, 22 Mar 2012 14:53:18 -0000" - */ - fn rfc822z(&self) -> ~str { - self.strftime("%a, %d %b %Y %T %z") - } - - /** - * Returns a time string formatted according to ISO 8601. - * - * local: "2012-02-22T07:53:18-07:00" - * utc: "2012-02-22T14:53:18Z" - */ - fn rfc3339(&self) -> ~str { - if self.tm_gmtoff == 0_i32 { - self.strftime("%Y-%m-%dT%H:%M:%SZ") - } else { - let s = self.strftime("%Y-%m-%dT%H:%M:%S"); - let sign = if self.tm_gmtoff > 0_i32 { '+' } else { '-' }; - let mut m = i32::abs(self.tm_gmtoff) / 60_i32; - let h = m / 60_i32; - m -= h * 60_i32; - s + fmt!("%c%02d:%02d", sign, h as int, m as int) - } - } -} - -priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> { - fn match_str(s: &str, pos: uint, needle: &str) -> bool { - let mut i = pos; - for str::each(needle) |ch| { - if s[i] != ch { - return false; - } - i += 1u; - } - return true; - } - - fn match_strs(ss: &str, pos: uint, strs: &[(~str, i32)]) - -> Option<(i32, uint)> { - let mut i = 0u; - let len = strs.len(); - while i < len { - let &(needle, value) = &strs[i]; - - if match_str(ss, pos, needle) { - return Some((value, pos + str::len(needle))); - } - i += 1u; - } - - None - } - - fn match_digits(ss: &str, pos: uint, digits: uint, ws: bool) - -> Option<(i32, uint)> { - let mut pos = pos; - let mut value = 0_i32; - - let mut i = 0u; - while i < digits { - let range = str::char_range_at(str::to_owned(ss), pos); - pos = range.next; - - match range.ch { - '0' .. '9' => { - value = value * 10_i32 + (range.ch as i32 - '0' as i32); - } - ' ' if ws => (), - _ => return None - } - i += 1u; - } - - Some((value, pos)) - } - - fn match_digits_in_range(ss: &str, pos: uint, digits: uint, ws: bool, - min: i32, max: i32) -> Option<(i32, uint)> { - match match_digits(ss, pos, digits, ws) { - Some((val, pos)) if val >= min && val <= max => { - Some((val, pos)) - } - _ => None - } - } - - fn parse_char(s: &str, pos: uint, c: char) -> Result<uint, ~str> { - let range = str::char_range_at(s, pos); - - if c == range.ch { - Ok(range.next) - } else { - Err(fmt!("Expected %?, found %?", - str::from_char(c), - str::from_char(range.ch))) - } - } - - fn parse_type(s: &str, pos: uint, ch: char, tm: &mut Tm) - -> Result<uint, ~str> { - match ch { - 'A' => match match_strs(s, pos, [ - (~"Sunday", 0_i32), - (~"Monday", 1_i32), - (~"Tuesday", 2_i32), - (~"Wednesday", 3_i32), - (~"Thursday", 4_i32), - (~"Friday", 5_i32), - (~"Saturday", 6_i32) - ]) { - Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) } - None => Err(~"Invalid day") - }, - 'a' => match match_strs(s, pos, [ - (~"Sun", 0_i32), - (~"Mon", 1_i32), - (~"Tue", 2_i32), - (~"Wed", 3_i32), - (~"Thu", 4_i32), - (~"Fri", 5_i32), - (~"Sat", 6_i32) - ]) { - Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) } - None => Err(~"Invalid day") - }, - 'B' => match match_strs(s, pos, [ - (~"January", 0_i32), - (~"February", 1_i32), - (~"March", 2_i32), - (~"April", 3_i32), - (~"May", 4_i32), - (~"June", 5_i32), - (~"July", 6_i32), - (~"August", 7_i32), - (~"September", 8_i32), - (~"October", 9_i32), - (~"November", 10_i32), - (~"December", 11_i32) - ]) { - Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) } - None => Err(~"Invalid month") - }, - 'b' | 'h' => match match_strs(s, pos, [ - (~"Jan", 0_i32), - (~"Feb", 1_i32), - (~"Mar", 2_i32), - (~"Apr", 3_i32), - (~"May", 4_i32), - (~"Jun", 5_i32), - (~"Jul", 6_i32), - (~"Aug", 7_i32), - (~"Sep", 8_i32), - (~"Oct", 9_i32), - (~"Nov", 10_i32), - (~"Dec", 11_i32) - ]) { - Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) } - None => Err(~"Invalid month") - }, - 'C' => match match_digits_in_range(s, pos, 2u, false, 0_i32, - 99_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_year += (v * 100_i32) - 1900_i32; - Ok(pos) - } - None => Err(~"Invalid year") - }, - 'c' => { - parse_type(s, pos, 'a', &mut *tm) - .chain(|pos| parse_char(s, pos, ' ')) - .chain(|pos| parse_type(s, pos, 'b', &mut *tm)) - .chain(|pos| parse_char(s, pos, ' ')) - .chain(|pos| parse_type(s, pos, 'e', &mut *tm)) - .chain(|pos| parse_char(s, pos, ' ')) - .chain(|pos| parse_type(s, pos, 'T', &mut *tm)) - .chain(|pos| parse_char(s, pos, ' ')) - .chain(|pos| parse_type(s, pos, 'Y', &mut *tm)) - } - 'D' | 'x' => { - parse_type(s, pos, 'm', &mut *tm) - .chain(|pos| parse_char(s, pos, '/')) - .chain(|pos| parse_type(s, pos, 'd', &mut *tm)) - .chain(|pos| parse_char(s, pos, '/')) - .chain(|pos| parse_type(s, pos, 'y', &mut *tm)) - } - 'd' => match match_digits_in_range(s, pos, 2u, false, 1_i32, - 31_i32) { - Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) } - None => Err(~"Invalid day of the month") - }, - 'e' => match match_digits_in_range(s, pos, 2u, true, 1_i32, - 31_i32) { - Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) } - None => Err(~"Invalid day of the month") - }, - 'F' => { - parse_type(s, pos, 'Y', &mut *tm) - .chain(|pos| parse_char(s, pos, '-')) - .chain(|pos| parse_type(s, pos, 'm', &mut *tm)) - .chain(|pos| parse_char(s, pos, '-')) - .chain(|pos| parse_type(s, pos, 'd', &mut *tm)) - } - 'H' => { - match match_digits_in_range(s, pos, 2u, false, 0_i32, 23_i32) { - Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) } - None => Err(~"Invalid hour") - } - } - 'I' => { - match match_digits_in_range(s, pos, 2u, false, 1_i32, 12_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_hour = if v == 12_i32 { 0_i32 } else { v }; - Ok(pos) - } - None => Err(~"Invalid hour") - } - } - 'j' => { - match match_digits_in_range(s, pos, 3u, false, 1_i32, 366_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_yday = v - 1_i32; - Ok(pos) - } - None => Err(~"Invalid day of year") - } - } - 'k' => { - match match_digits_in_range(s, pos, 2u, true, 0_i32, 23_i32) { - Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) } - None => Err(~"Invalid hour") - } - } - 'l' => { - match match_digits_in_range(s, pos, 2u, true, 1_i32, 12_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_hour = if v == 12_i32 { 0_i32 } else { v }; - Ok(pos) - } - None => Err(~"Invalid hour") - } - } - 'M' => { - match match_digits_in_range(s, pos, 2u, false, 0_i32, 59_i32) { - Some(item) => { let (v, pos) = item; tm.tm_min = v; Ok(pos) } - None => Err(~"Invalid minute") - } - } - 'm' => { - match match_digits_in_range(s, pos, 2u, false, 1_i32, 12_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_mon = v - 1_i32; - Ok(pos) - } - None => Err(~"Invalid month") - } - } - 'n' => parse_char(s, pos, '\n'), - 'P' => match match_strs(s, pos, - [(~"am", 0_i32), (~"pm", 12_i32)]) { - - Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) } - None => Err(~"Invalid hour") - }, - 'p' => match match_strs(s, pos, - [(~"AM", 0_i32), (~"PM", 12_i32)]) { - - Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) } - None => Err(~"Invalid hour") - }, - 'R' => { - parse_type(s, pos, 'H', &mut *tm) - .chain(|pos| parse_char(s, pos, ':')) - .chain(|pos| parse_type(s, pos, 'M', &mut *tm)) - } - 'r' => { - parse_type(s, pos, 'I', &mut *tm) - .chain(|pos| parse_char(s, pos, ':')) - .chain(|pos| parse_type(s, pos, 'M', &mut *tm)) - .chain(|pos| parse_char(s, pos, ':')) - .chain(|pos| parse_type(s, pos, 'S', &mut *tm)) - .chain(|pos| parse_char(s, pos, ' ')) - .chain(|pos| parse_type(s, pos, 'p', &mut *tm)) - } - 'S' => { - match match_digits_in_range(s, pos, 2u, false, 0_i32, 60_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_sec = v; - Ok(pos) - } - None => Err(~"Invalid second") - } - } - //'s' {} - 'T' | 'X' => { - parse_type(s, pos, 'H', &mut *tm) - .chain(|pos| parse_char(s, pos, ':')) - .chain(|pos| parse_type(s, pos, 'M', &mut *tm)) - .chain(|pos| parse_char(s, pos, ':')) - .chain(|pos| parse_type(s, pos, 'S', &mut *tm)) - } - 't' => parse_char(s, pos, '\t'), - 'u' => { - match match_digits_in_range(s, pos, 1u, false, 1_i32, 7_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_wday = if v == 7 { 0 } else { v }; - Ok(pos) - } - None => Err(~"Invalid day of week") - } - } - 'v' => { - parse_type(s, pos, 'e', &mut *tm) - .chain(|pos| parse_char(s, pos, '-')) - .chain(|pos| parse_type(s, pos, 'b', &mut *tm)) - .chain(|pos| parse_char(s, pos, '-')) - .chain(|pos| parse_type(s, pos, 'Y', &mut *tm)) - } - //'W' {} - 'w' => { - match match_digits_in_range(s, pos, 1u, false, 0_i32, 6_i32) { - Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) } - None => Err(~"Invalid day of week") - } - } - //'X' {} - //'x' {} - 'Y' => { - match match_digits(s, pos, 4u, false) { - Some(item) => { - let (v, pos) = item; - tm.tm_year = v - 1900_i32; - Ok(pos) - } - None => Err(~"Invalid year") - } - } - 'y' => { - match match_digits_in_range(s, pos, 2u, false, 0_i32, 99_i32) { - Some(item) => { - let (v, pos) = item; - tm.tm_year = v; - Ok(pos) - } - None => Err(~"Invalid year") - } - } - 'Z' => { - if match_str(s, pos, "UTC") || match_str(s, pos, "GMT") { - tm.tm_gmtoff = 0_i32; - tm.tm_zone = ~"UTC"; - Ok(pos + 3u) - } else { - // It's odd, but to maintain compatibility with c's - // strptime we ignore the timezone. - let mut pos = pos; - let len = str::len(s); - while pos < len { - let range = str::char_range_at(s, pos); - pos = range.next; - if range.ch == ' ' { break; } - } - - Ok(pos) - } - } - 'z' => { - let range = str::char_range_at(s, pos); - - if range.ch == '+' || range.ch == '-' { - match match_digits(s, range.next, 4u, false) { - Some(item) => { - let (v, pos) = item; - if v == 0_i32 { - tm.tm_gmtoff = 0_i32; - tm.tm_zone = ~"UTC"; - } - - Ok(pos) - } - None => Err(~"Invalid zone offset") - } - } else { - Err(~"Invalid zone offset") - } - } - '%' => parse_char(s, pos, '%'), - ch => { - Err(fmt!("unknown formatting type: %?", str::from_char(ch))) - } - } - } - - do io::with_str_reader(str::to_owned(format)) |rdr| { - let mut tm = Tm { - tm_sec: 0_i32, - tm_min: 0_i32, - tm_hour: 0_i32, - tm_mday: 0_i32, - tm_mon: 0_i32, - tm_year: 0_i32, - tm_wday: 0_i32, - tm_yday: 0_i32, - tm_isdst: 0_i32, - tm_gmtoff: 0_i32, - tm_zone: ~"", - tm_nsec: 0_i32, - }; - let mut pos = 0u; - let len = str::len(s); - let mut result = Err(~"Invalid time"); - - while !rdr.eof() && pos < len { - let range = str::char_range_at(s, pos); - let ch = range.ch; - let next = range.next; - - match rdr.read_char() { - '%' => { - match parse_type(s, pos, rdr.read_char(), &mut tm) { - Ok(next) => pos = next, - Err(e) => { result = Err(e); break; } - } - }, - c => { - if c != ch { break } - pos = next; - } - } - } - - if pos == len && rdr.eof() { - Ok(Tm { - tm_sec: tm.tm_sec, - tm_min: tm.tm_min, - tm_hour: tm.tm_hour, - tm_mday: tm.tm_mday, - tm_mon: tm.tm_mon, - tm_year: tm.tm_year, - tm_wday: tm.tm_wday, - tm_yday: tm.tm_yday, - tm_isdst: tm.tm_isdst, - tm_gmtoff: tm.tm_gmtoff, - tm_zone: copy tm.tm_zone, - tm_nsec: tm.tm_nsec, - }) - } else { result } - } -} - -priv fn do_strftime(format: &str, tm: &Tm) -> ~str { - fn parse_type(ch: char, tm: &Tm) -> ~str { - //FIXME (#2350): Implement missing types. - let die = || fmt!("strftime: can't understand this format %c ", ch); - match ch { - 'A' => match tm.tm_wday as int { - 0 => ~"Sunday", - 1 => ~"Monday", - 2 => ~"Tuesday", - 3 => ~"Wednesday", - 4 => ~"Thursday", - 5 => ~"Friday", - 6 => ~"Saturday", - _ => die() - }, - 'a' => match tm.tm_wday as int { - 0 => ~"Sun", - 1 => ~"Mon", - 2 => ~"Tue", - 3 => ~"Wed", - 4 => ~"Thu", - 5 => ~"Fri", - 6 => ~"Sat", - _ => die() - }, - 'B' => match tm.tm_mon as int { - 0 => ~"January", - 1 => ~"February", - 2 => ~"March", - 3 => ~"April", - 4 => ~"May", - 5 => ~"June", - 6 => ~"July", - 7 => ~"August", - 8 => ~"September", - 9 => ~"October", - 10 => ~"November", - 11 => ~"December", - _ => die() - }, - 'b' | 'h' => match tm.tm_mon as int { - 0 => ~"Jan", - 1 => ~"Feb", - 2 => ~"Mar", - 3 => ~"Apr", - 4 => ~"May", - 5 => ~"Jun", - 6 => ~"Jul", - 7 => ~"Aug", - 8 => ~"Sep", - 9 => ~"Oct", - 10 => ~"Nov", - 11 => ~"Dec", - _ => die() - }, - 'C' => fmt!("%02d", (tm.tm_year as int + 1900) / 100), - 'c' => { - fmt!("%s %s %s %s %s", - parse_type('a', tm), - parse_type('b', tm), - parse_type('e', tm), - parse_type('T', tm), - parse_type('Y', tm)) - } - 'D' | 'x' => { - fmt!("%s/%s/%s", - parse_type('m', tm), - parse_type('d', tm), - parse_type('y', tm)) - } - 'd' => fmt!("%02d", tm.tm_mday as int), - 'e' => fmt!("%2d", tm.tm_mday as int), - 'F' => { - fmt!("%s-%s-%s", - parse_type('Y', tm), - parse_type('m', tm), - parse_type('d', tm)) - } - //'G' {} - //'g' {} - 'H' => fmt!("%02d", tm.tm_hour as int), - 'I' => { - let mut h = tm.tm_hour as int; - if h == 0 { h = 12 } - if h > 12 { h -= 12 } - fmt!("%02d", h) - } - 'j' => fmt!("%03d", tm.tm_yday as int + 1), - 'k' => fmt!("%2d", tm.tm_hour as int), - 'l' => { - let mut h = tm.tm_hour as int; - if h == 0 { h = 12 } - if h > 12 { h -= 12 } - fmt!("%2d", h) - } - 'M' => fmt!("%02d", tm.tm_min as int), - 'm' => fmt!("%02d", tm.tm_mon as int + 1), - 'n' => ~"\n", - 'P' => if (tm.tm_hour as int) < 12 { ~"am" } else { ~"pm" }, - 'p' => if (tm.tm_hour as int) < 12 { ~"AM" } else { ~"PM" }, - 'R' => { - fmt!("%s:%s", - parse_type('H', tm), - parse_type('M', tm)) - } - 'r' => { - fmt!("%s:%s:%s %s", - parse_type('I', tm), - parse_type('M', tm), - parse_type('S', tm), - parse_type('p', tm)) - } - 'S' => fmt!("%02d", tm.tm_sec as int), - 's' => fmt!("%d", tm.to_timespec().sec as int), - 'T' | 'X' => { - fmt!("%s:%s:%s", - parse_type('H', tm), - parse_type('M', tm), - parse_type('S', tm)) - } - 't' => ~"\t", - //'U' {} - 'u' => { - let i = tm.tm_wday as int; - int::to_str(if i == 0 { 7 } else { i }) - } - //'V' {} - 'v' => { - fmt!("%s-%s-%s", - parse_type('e', tm), - parse_type('b', tm), - parse_type('Y', tm)) - } - //'W' {} - 'w' => int::to_str(tm.tm_wday as int), - //'X' {} - //'x' {} - 'Y' => int::to_str(tm.tm_year as int + 1900), - 'y' => fmt!("%02d", (tm.tm_year as int + 1900) % 100), - 'Z' => copy tm.tm_zone, - 'z' => { - let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' }; - let mut m = i32::abs(tm.tm_gmtoff) / 60_i32; - let h = m / 60_i32; - m -= h * 60_i32; - fmt!("%c%02d%02d", sign, h as int, m as int) - } - //'+' {} - '%' => ~"%", - _ => die() - } - } - - let mut buf = ~""; - - do io::with_str_reader(str::to_owned(format)) |rdr| { - while !rdr.eof() { - match rdr.read_char() { - '%' => buf += parse_type(rdr.read_char(), tm), - ch => str::push_char(&mut buf, ch) - } - } - } - - buf -} - -#[cfg(test)] -mod tests { - use time::*; - - use core::float; - use core::os; - use core::result; - use core::result::{Err, Ok}; - use core::str; - - fn test_get_time() { - static some_recent_date: i64 = 1325376000i64; // 2012-01-01T00:00:00Z - static some_future_date: i64 = 1577836800i64; // 2020-01-01T00:00:00Z - - let tv1 = get_time(); - debug!("tv1=%? sec + %? nsec", tv1.sec as uint, tv1.nsec as uint); - - assert!(tv1.sec > some_recent_date); - assert!(tv1.nsec < 1000000000i32); - - let tv2 = get_time(); - debug!("tv2=%? sec + %? nsec", tv2.sec as uint, tv2.nsec as uint); - - assert!(tv2.sec >= tv1.sec); - assert!(tv2.sec < some_future_date); - assert!(tv2.nsec < 1000000000i32); - if tv2.sec == tv1.sec { - assert!(tv2.nsec >= tv1.nsec); - } - } - - fn test_precise_time() { - let s0 = precise_time_s(); - let ns1 = precise_time_ns(); - - debug!("s0=%s sec", float::to_str_digits(s0, 9u)); - assert!(s0 > 0.); - let ns0 = (s0 * 1000000000.) as u64; - debug!("ns0=%? ns", ns0); - - debug!("ns1=%? ns", ns0); - assert!(ns1 >= ns0); - - let ns2 = precise_time_ns(); - debug!("ns2=%? ns", ns0); - assert!(ns2 >= ns1); - } - - fn test_at_utc() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let utc = at_utc(time); - - assert!(utc.tm_sec == 30_i32); - assert!(utc.tm_min == 31_i32); - assert!(utc.tm_hour == 23_i32); - assert!(utc.tm_mday == 13_i32); - assert!(utc.tm_mon == 1_i32); - assert!(utc.tm_year == 109_i32); - assert!(utc.tm_wday == 5_i32); - assert!(utc.tm_yday == 43_i32); - assert!(utc.tm_isdst == 0_i32); - assert!(utc.tm_gmtoff == 0_i32); - assert!(utc.tm_zone == ~"UTC"); - assert!(utc.tm_nsec == 54321_i32); - } - - fn test_at() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let local = at(time); - - error!("time_at: %?", local); - - assert!(local.tm_sec == 30_i32); - assert!(local.tm_min == 31_i32); - assert!(local.tm_hour == 15_i32); - assert!(local.tm_mday == 13_i32); - assert!(local.tm_mon == 1_i32); - assert!(local.tm_year == 109_i32); - assert!(local.tm_wday == 5_i32); - assert!(local.tm_yday == 43_i32); - assert!(local.tm_isdst == 0_i32); - assert!(local.tm_gmtoff == -28800_i32); - - // FIXME (#2350): We should probably standardize on the timezone - // abbreviation. - let zone = &local.tm_zone; - assert!(*zone == ~"PST" || *zone == ~"Pacific Standard Time"); - - assert!(local.tm_nsec == 54321_i32); - } - - fn test_to_timespec() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let utc = at_utc(time); - - assert_eq!(utc.to_timespec(), time); - assert_eq!(utc.to_local().to_timespec(), time); - } - - fn test_conversions() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let utc = at_utc(time); - let local = at(time); - - assert!(local.to_local() == local); - assert!(local.to_utc() == utc); - assert!(local.to_utc().to_local() == local); - assert!(utc.to_utc() == utc); - assert!(utc.to_local() == local); - assert!(utc.to_local().to_utc() == utc); - } - - fn test_strptime() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - match strptime(~"", ~"") { - Ok(ref tm) => { - assert!(tm.tm_sec == 0_i32); - assert!(tm.tm_min == 0_i32); - assert!(tm.tm_hour == 0_i32); - assert!(tm.tm_mday == 0_i32); - assert!(tm.tm_mon == 0_i32); - assert!(tm.tm_year == 0_i32); - assert!(tm.tm_wday == 0_i32); - assert!(tm.tm_isdst == 0_i32); - assert!(tm.tm_gmtoff == 0_i32); - assert!(tm.tm_zone == ~""); - assert!(tm.tm_nsec == 0_i32); - } - Err(_) => () - } - - let format = ~"%a %b %e %T %Y"; - assert_eq!(strptime(~"", format), Err(~"Invalid time")); - assert!(strptime(~"Fri Feb 13 15:31:30", format) - == Err(~"Invalid time")); - - match strptime(~"Fri Feb 13 15:31:30 2009", format) { - Err(copy e) => fail!(e), - Ok(ref tm) => { - assert!(tm.tm_sec == 30_i32); - assert!(tm.tm_min == 31_i32); - assert!(tm.tm_hour == 15_i32); - assert!(tm.tm_mday == 13_i32); - assert!(tm.tm_mon == 1_i32); - assert!(tm.tm_year == 109_i32); - assert!(tm.tm_wday == 5_i32); - assert!(tm.tm_yday == 0_i32); - assert!(tm.tm_isdst == 0_i32); - assert!(tm.tm_gmtoff == 0_i32); - assert!(tm.tm_zone == ~""); - assert!(tm.tm_nsec == 0_i32); - } - } - - fn test(s: &str, format: &str) -> bool { - match strptime(s, format) { - Ok(ref tm) => tm.strftime(format) == str::to_owned(s), - Err(copy e) => fail!(e) - } - } - - for [ - ~"Sunday", - ~"Monday", - ~"Tuesday", - ~"Wednesday", - ~"Thursday", - ~"Friday", - ~"Saturday" - ].each |day| { - assert!(test(*day, ~"%A")); - } - - for [ - ~"Sun", - ~"Mon", - ~"Tue", - ~"Wed", - ~"Thu", - ~"Fri", - ~"Sat" - ].each |day| { - assert!(test(*day, ~"%a")); - } - - for [ - ~"January", - ~"February", - ~"March", - ~"April", - ~"May", - ~"June", - ~"July", - ~"August", - ~"September", - ~"October", - ~"November", - ~"December" - ].each |day| { - assert!(test(*day, ~"%B")); - } - - for [ - ~"Jan", - ~"Feb", - ~"Mar", - ~"Apr", - ~"May", - ~"Jun", - ~"Jul", - ~"Aug", - ~"Sep", - ~"Oct", - ~"Nov", - ~"Dec" - ].each |day| { - assert!(test(*day, ~"%b")); - } - - assert!(test(~"19", ~"%C")); - assert!(test(~"Fri Feb 13 23:31:30 2009", ~"%c")); - assert!(test(~"02/13/09", ~"%D")); - assert!(test(~"03", ~"%d")); - assert!(test(~"13", ~"%d")); - assert!(test(~" 3", ~"%e")); - assert!(test(~"13", ~"%e")); - assert!(test(~"2009-02-13", ~"%F")); - assert!(test(~"03", ~"%H")); - assert!(test(~"13", ~"%H")); - assert!(test(~"03", ~"%I")); // FIXME (#2350): flesh out - assert!(test(~"11", ~"%I")); // FIXME (#2350): flesh out - assert!(test(~"044", ~"%j")); - assert!(test(~" 3", ~"%k")); - assert!(test(~"13", ~"%k")); - assert!(test(~" 1", ~"%l")); - assert!(test(~"11", ~"%l")); - assert!(test(~"03", ~"%M")); - assert!(test(~"13", ~"%M")); - assert!(test(~"\n", ~"%n")); - assert!(test(~"am", ~"%P")); - assert!(test(~"pm", ~"%P")); - assert!(test(~"AM", ~"%p")); - assert!(test(~"PM", ~"%p")); - assert!(test(~"23:31", ~"%R")); - assert!(test(~"11:31:30 AM", ~"%r")); - assert!(test(~"11:31:30 PM", ~"%r")); - assert!(test(~"03", ~"%S")); - assert!(test(~"13", ~"%S")); - assert!(test(~"15:31:30", ~"%T")); - assert!(test(~"\t", ~"%t")); - assert!(test(~"1", ~"%u")); - assert!(test(~"7", ~"%u")); - assert!(test(~"13-Feb-2009", ~"%v")); - assert!(test(~"0", ~"%w")); - assert!(test(~"6", ~"%w")); - assert!(test(~"2009", ~"%Y")); - assert!(test(~"09", ~"%y")); - assert!(result::unwrap(strptime(~"UTC", ~"%Z")).tm_zone == - ~"UTC"); - assert!(result::unwrap(strptime(~"PST", ~"%Z")).tm_zone == - ~""); - assert!(result::unwrap(strptime(~"-0000", ~"%z")).tm_gmtoff == - 0); - assert!(result::unwrap(strptime(~"-0800", ~"%z")).tm_gmtoff == - 0); - assert!(test(~"%", ~"%%")); - } - - fn test_ctime() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let utc = at_utc(time); - let local = at(time); - - error!("test_ctime: %? %?", utc.ctime(), local.ctime()); - - assert_eq!(utc.ctime(), ~"Fri Feb 13 23:31:30 2009"); - assert_eq!(local.ctime(), ~"Fri Feb 13 15:31:30 2009"); - } - - fn test_strftime() { - os::setenv(~"TZ", ~"America/Los_Angeles"); - tzset(); - - let time = ::time::Timespec::new(1234567890, 54321); - let utc = at_utc(time); - let local = at(time); - - assert_eq!(local.strftime(~""), ~""); - assert_eq!(local.strftime(~"%A"), ~"Friday"); - assert_eq!(local.strftime(~"%a"), ~"Fri"); - assert_eq!(local.strftime(~"%B"), ~"February"); - assert_eq!(local.strftime(~"%b"), ~"Feb"); - assert_eq!(local.strftime(~"%C"), ~"20"); - assert_eq!(local.strftime(~"%c"), ~"Fri Feb 13 15:31:30 2009"); - assert_eq!(local.strftime(~"%D"), ~"02/13/09"); - assert_eq!(local.strftime(~"%d"), ~"13"); - assert_eq!(local.strftime(~"%e"), ~"13"); - assert_eq!(local.strftime(~"%F"), ~"2009-02-13"); - // assert!(local.strftime("%G") == "2009"); - // assert!(local.strftime("%g") == "09"); - assert_eq!(local.strftime(~"%H"), ~"15"); - assert_eq!(local.strftime(~"%I"), ~"03"); - assert_eq!(local.strftime(~"%j"), ~"044"); - assert_eq!(local.strftime(~"%k"), ~"15"); - assert_eq!(local.strftime(~"%l"), ~" 3"); - assert_eq!(local.strftime(~"%M"), ~"31"); - assert_eq!(local.strftime(~"%m"), ~"02"); - assert_eq!(local.strftime(~"%n"), ~"\n"); - assert_eq!(local.strftime(~"%P"), ~"pm"); - assert_eq!(local.strftime(~"%p"), ~"PM"); - assert_eq!(local.strftime(~"%R"), ~"15:31"); - assert_eq!(local.strftime(~"%r"), ~"03:31:30 PM"); - assert_eq!(local.strftime(~"%S"), ~"30"); - assert_eq!(local.strftime(~"%s"), ~"1234567890"); - assert_eq!(local.strftime(~"%T"), ~"15:31:30"); - assert_eq!(local.strftime(~"%t"), ~"\t"); - // assert!(local.strftime("%U") == "06"); - assert_eq!(local.strftime(~"%u"), ~"5"); - // assert!(local.strftime("%V") == "07"); - assert_eq!(local.strftime(~"%v"), ~"13-Feb-2009"); - // assert!(local.strftime("%W") == "06"); - assert_eq!(local.strftime(~"%w"), ~"5"); - // handle "%X" - // handle "%x" - assert_eq!(local.strftime(~"%Y"), ~"2009"); - assert_eq!(local.strftime(~"%y"), ~"09"); - - // FIXME (#2350): We should probably standardize on the timezone - // abbreviation. - let zone = local.strftime(~"%Z"); - assert!(zone == ~"PST" || zone == ~"Pacific Standard Time"); - - assert_eq!(local.strftime(~"%z"), ~"-0800"); - assert_eq!(local.strftime(~"%%"), ~"%"); - - // FIXME (#2350): We should probably standardize on the timezone - // abbreviation. - let rfc822 = local.rfc822(); - let prefix = ~"Fri, 13 Feb 2009 15:31:30 "; - assert!(rfc822 == prefix + ~"PST" || - rfc822 == prefix + ~"Pacific Standard Time"); - - assert_eq!(local.ctime(), ~"Fri Feb 13 15:31:30 2009"); - assert_eq!(local.rfc822z(), ~"Fri, 13 Feb 2009 15:31:30 -0800"); - assert_eq!(local.rfc3339(), ~"2009-02-13T15:31:30-08:00"); - - assert_eq!(utc.ctime(), ~"Fri Feb 13 23:31:30 2009"); - assert_eq!(utc.rfc822(), ~"Fri, 13 Feb 2009 23:31:30 GMT"); - assert_eq!(utc.rfc822z(), ~"Fri, 13 Feb 2009 23:31:30 -0000"); - assert_eq!(utc.rfc3339(), ~"2009-02-13T23:31:30Z"); - } - - fn test_timespec_eq_ord() { - use core::cmp::{eq, ge, gt, le, lt, ne}; - - let a = &Timespec::new(-2, 1); - let b = &Timespec::new(-1, 2); - let c = &Timespec::new(1, 2); - let d = &Timespec::new(2, 1); - let e = &Timespec::new(2, 1); - - assert!(eq(d, e)); - assert!(ne(c, e)); - - assert!(lt(a, b)); - assert!(lt(b, c)); - assert!(lt(c, d)); - - assert!(le(a, b)); - assert!(le(b, c)); - assert!(le(c, d)); - assert!(le(d, e)); - assert!(le(e, d)); - - assert!(ge(b, a)); - assert!(ge(c, b)); - assert!(ge(d, c)); - assert!(ge(e, d)); - assert!(ge(d, e)); - - assert!(gt(b, a)); - assert!(gt(c, b)); - assert!(gt(d, c)); - } - - #[test] - fn run_tests() { - // The tests race on tzset. So instead of having many independent - // tests, we will just call the functions now. - test_get_time(); - test_precise_time(); - test_at_utc(); - test_at(); - test_to_timespec(); - test_conversions(); - test_strptime(); - test_ctime(); - test_strftime(); - test_timespec_eq_ord(); - } -} diff --git a/src/libstd/timer.rs b/src/libstd/timer.rs deleted file mode 100644 index 092c718f1ce..00000000000 --- a/src/libstd/timer.rs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Utilities that leverage libuv's `uv_timer_*` API - -use uv; -use uv::iotask; -use uv::iotask::IoTask; - -use core::cast::transmute; -use core::cast; -use core::comm::{stream, Chan, SharedChan, Port, select2i}; -use core::libc::c_void; -use core::libc; - -/** - * Wait for timeout period then send provided value over a channel - * - * This call returns immediately. Useful as the building block for a number - * of higher-level timer functions. - * - * Is not guaranteed to wait for exactly the specified time, but will wait - * for *at least* that period of time. - * - * # Arguments - * - * * `hl_loop` - a `uv::hl::high_level_loop` that the tcp request will run on - * * msecs - a timeout period, in milliseconds, to wait - * * ch - a channel of type T to send a `val` on - * * val - a value of type T to send over the provided `ch` - */ -pub fn delayed_send<T:Owned>(iotask: &IoTask, - msecs: uint, - ch: &Chan<T>, - val: T) { - let (timer_done_po, timer_done_ch) = stream::<()>(); - let timer_done_ch = SharedChan::new(timer_done_ch); - let timer = uv::ll::timer_t(); - let timer_ptr: *uv::ll::uv_timer_t = &timer; - do iotask::interact(iotask) |loop_ptr| { - unsafe { - let init_result = uv::ll::timer_init(loop_ptr, timer_ptr); - if (init_result == 0i32) { - let start_result = uv::ll::timer_start( - timer_ptr, delayed_send_cb, msecs, 0u); - if (start_result == 0i32) { - // Note: putting the channel into a ~ - // to cast to *c_void - let timer_done_ch_clone = ~timer_done_ch.clone(); - let timer_done_ch_ptr = transmute::< - ~SharedChan<()>, *c_void>( - timer_done_ch_clone); - uv::ll::set_data_for_uv_handle( - timer_ptr, - timer_done_ch_ptr); - } else { - let error_msg = uv::ll::get_last_err_info( - loop_ptr); - fail!("timer::delayed_send() start failed: %s", error_msg); - } - } else { - let error_msg = uv::ll::get_last_err_info(loop_ptr); - fail!("timer::delayed_send() init failed: %s", error_msg); - } - } - }; - // delayed_send_cb has been processed by libuv - timer_done_po.recv(); - // notify the caller immediately - ch.send(val); - // uv_close for this timer has been processed - timer_done_po.recv(); -} - -/** - * Blocks the current task for (at least) the specified time period. - * - * Is not guaranteed to sleep for exactly the specified time, but will sleep - * for *at least* that period of time. - * - * # Arguments - * - * * `iotask` - a `uv::iotask` that the tcp request will run on - * * msecs - an amount of time, in milliseconds, for the current task to block - */ -pub fn sleep(iotask: &IoTask, msecs: uint) { - let (exit_po, exit_ch) = stream::<()>(); - delayed_send(iotask, msecs, &exit_ch, ()); - exit_po.recv(); -} - -/** - * Receive on a port for (up to) a specified time, then return an `Option<T>` - * - * This call will block to receive on the provided port for up to the - * specified timeout. Depending on whether the provided port receives in that - * time period, `recv_timeout` will return an `Option<T>` representing the - * result. - * - * # Arguments - * - * * `iotask' - `uv::iotask` that the tcp request will run on - * * msecs - an mount of time, in milliseconds, to wait to receive - * * wait_port - a `core::comm::port<T>` to receive on - * - * # Returns - * - * An `Option<T>` representing the outcome of the call. If the call `recv`'d - * on the provided port in the allotted timeout period, then the result will - * be a `Some(T)`. If not, then `None` will be returned. - */ -pub fn recv_timeout<T:Copy + Owned>(iotask: &IoTask, - msecs: uint, - wait_po: &Port<T>) - -> Option<T> { - let mut (timeout_po, timeout_ch) = stream::<()>(); - delayed_send(iotask, msecs, &timeout_ch, ()); - - // XXX: Workaround due to ports and channels not being &mut. They should - // be. - unsafe { - let wait_po = cast::transmute_mut(wait_po); - - // FIXME: This could be written clearer (#2618) - either::either( - |_| { - None - }, |_| { - Some(wait_po.recv()) - }, &select2i(&mut timeout_po, wait_po) - ) - } -} - -// INTERNAL API -extern fn delayed_send_cb(handle: *uv::ll::uv_timer_t, status: libc::c_int) { - unsafe { - debug!( - "delayed_send_cb handle %? status %?", handle, status); - // Faking a borrowed pointer to our ~SharedChan - let timer_done_ch_ptr: &*c_void = &uv::ll::get_data_for_uv_handle( - handle); - let timer_done_ch_ptr = transmute::<&*c_void, &~SharedChan<()>>( - timer_done_ch_ptr); - let stop_result = uv::ll::timer_stop(handle); - if (stop_result == 0i32) { - timer_done_ch_ptr.send(()); - uv::ll::close(handle, delayed_send_close_cb); - } else { - let loop_ptr = uv::ll::get_loop_for_uv_handle(handle); - let error_msg = uv::ll::get_last_err_info(loop_ptr); - fail!("timer::sleep() init failed: %s", error_msg); - } - } -} - -extern fn delayed_send_close_cb(handle: *uv::ll::uv_timer_t) { - unsafe { - debug!("delayed_send_close_cb handle %?", handle); - let timer_done_ch_ptr = uv::ll::get_data_for_uv_handle(handle); - let timer_done_ch = transmute::<*c_void, ~SharedChan<()>>( - timer_done_ch_ptr); - timer_done_ch.send(()); - } -} - -#[cfg(test)] -mod test { - use timer::*; - use uv; - use core::cell::Cell; - use core::rand::RngUtil; - use core::pipes::{stream, SharedChan}; - - #[test] - fn test_gl_timer_simple_sleep_test() { - let hl_loop = &uv::global_loop::get(); - sleep(hl_loop, 1u); - } - - #[test] - fn test_gl_timer_sleep_stress1() { - let hl_loop = &uv::global_loop::get(); - for 50u.times { - sleep(hl_loop, 1u); - } - } - - #[test] - fn test_gl_timer_sleep_stress2() { - let (po, ch) = stream(); - let ch = SharedChan::new(ch); - let hl_loop = &uv::global_loop::get(); - - let repeat = 20u; - let spec = { - - ~[(1u, 20u), - (10u, 10u), - (20u, 2u)] - - }; - - for repeat.times { - let ch = ch.clone(); - for spec.each |spec| { - let (times, maxms) = *spec; - let ch = ch.clone(); - let hl_loop_clone = hl_loop.clone(); - do task::spawn { - use core::rand::*; - let mut rng = rng(); - for times.times { - sleep(&hl_loop_clone, rng.next() as uint % maxms); - } - ch.send(()); - } - } - } - - for (repeat * spec.len()).times { - po.recv() - } - } - - // Because valgrind serializes multithreaded programs it can - // make timing-sensitive tests fail in wierd ways. In these - // next test we run them many times and expect them to pass - // the majority of tries. - - #[test] - #[cfg(ignore)] - fn test_gl_timer_recv_timeout_before_time_passes() { - let times = 100; - let mut successes = 0; - let mut failures = 0; - let hl_loop = uv::global_loop::get(); - - for (times as uint).times { - task::yield(); - - let expected = rand::rng().gen_str(16u); - let (test_po, test_ch) = stream::<~str>(); - - do task::spawn() { - delayed_send(hl_loop, 1u, &test_ch, expected); - }; - - match recv_timeout(hl_loop, 10u, &test_po) { - Some(val) => { - assert_eq!(val, expected); - successes += 1; - } - _ => failures += 1 - }; - } - - assert!(successes > times / 2); - } - - #[test] - fn test_gl_timer_recv_timeout_after_time_passes() { - let times = 100; - let mut successes = 0; - let mut failures = 0; - let hl_loop = uv::global_loop::get(); - - for (times as uint).times { - let mut rng = rand::rng(); - let expected = Cell(rng.gen_str(16u)); - let (test_po, test_ch) = stream::<~str>(); - let hl_loop_clone = hl_loop.clone(); - do task::spawn() { - delayed_send(&hl_loop_clone, 50u, &test_ch, expected.take()); - }; - - match recv_timeout(&hl_loop, 1u, &test_po) { - None => successes += 1, - _ => failures += 1 - }; - } - - assert!(successes > times / 2); - } -} diff --git a/src/libstd/to_bytes.rs b/src/libstd/to_bytes.rs new file mode 100644 index 00000000000..5b66e94c1b4 --- /dev/null +++ b/src/libstd/to_bytes.rs @@ -0,0 +1,354 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +The `ToBytes` and `IterBytes` traits + +*/ + +use io; +use io::Writer; +use option::{None, Option, Some}; +use old_iter::BaseIter; +use str; + +pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool; + +/** + * A trait to implement in order to make a type hashable; + * This works in combination with the trait `Hash::Hash`, and + * may in the future be merged with that trait or otherwise + * modified when default methods and trait inheritence are + * completed. + */ +pub trait IterBytes { + /** + * Call the provided callback `f` one or more times with + * byte-slices that should be used when computing a hash + * value or otherwise "flattening" the structure into + * a sequence of bytes. The `lsb0` parameter conveys + * whether the caller is asking for little-endian bytes + * (`true`) or big-endian (`false`); this should only be + * relevant in implementations that represent a single + * multi-byte datum such as a 32 bit integer or 64 bit + * floating-point value. It can be safely ignored for + * larger structured types as they are usually processed + * left-to-right in declaration order, regardless of + * underlying memory endianness. + */ + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool; +} + +impl IterBytes for bool { + #[inline(always)] + fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool { + f([ + *self as u8 + ]) + } +} + +impl IterBytes for u8 { + #[inline(always)] + fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool { + f([ + *self + ]) + } +} + +impl IterBytes for u16 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + if lsb0 { + f([ + *self as u8, + (*self >> 8) as u8 + ]) + } else { + f([ + (*self >> 8) as u8, + *self as u8 + ]) + } + } +} + +impl IterBytes for u32 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + if lsb0 { + f([ + *self as u8, + (*self >> 8) as u8, + (*self >> 16) as u8, + (*self >> 24) as u8, + ]) + } else { + f([ + (*self >> 24) as u8, + (*self >> 16) as u8, + (*self >> 8) as u8, + *self as u8 + ]) + } + } +} + +impl IterBytes for u64 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + if lsb0 { + f([ + *self as u8, + (*self >> 8) as u8, + (*self >> 16) as u8, + (*self >> 24) as u8, + (*self >> 32) as u8, + (*self >> 40) as u8, + (*self >> 48) as u8, + (*self >> 56) as u8 + ]) + } else { + f([ + (*self >> 56) as u8, + (*self >> 48) as u8, + (*self >> 40) as u8, + (*self >> 32) as u8, + (*self >> 24) as u8, + (*self >> 16) as u8, + (*self >> 8) as u8, + *self as u8 + ]) + } + } +} + +impl IterBytes for i8 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u8).iter_bytes(lsb0, f) + } +} + +impl IterBytes for i16 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u16).iter_bytes(lsb0, f) + } +} + +impl IterBytes for i32 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u32).iter_bytes(lsb0, f) + } +} + +impl IterBytes for i64 { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u64).iter_bytes(lsb0, f) + } +} + +impl IterBytes for char { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u32).iter_bytes(lsb0, f) + } +} + +#[cfg(target_word_size = "32")] +impl IterBytes for uint { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u32).iter_bytes(lsb0, f) + } +} + +#[cfg(target_word_size = "64")] +impl IterBytes for uint { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as u64).iter_bytes(lsb0, f) + } +} + +impl IterBytes for int { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as uint).iter_bytes(lsb0, f) + } +} + +impl<'self,A:IterBytes> IterBytes for &'self [A] { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + self.each(|elt| elt.iter_bytes(lsb0, |b| f(b))) + } +} + +impl<A:IterBytes,B:IterBytes> IterBytes for (A,B) { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + match *self { + (ref a, ref b) => { a.iter_bytes(lsb0, f) && b.iter_bytes(lsb0, f) } + } + } +} + +impl<A:IterBytes,B:IterBytes,C:IterBytes> IterBytes for (A,B,C) { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + match *self { + (ref a, ref b, ref c) => { + a.iter_bytes(lsb0, f) && b.iter_bytes(lsb0, f) && c.iter_bytes(lsb0, f) + } + } + } +} + +// Move this to vec, probably. +fn borrow<'x,A>(a: &'x [A]) -> &'x [A] { + a +} + +impl<A:IterBytes> IterBytes for ~[A] { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + borrow(*self).iter_bytes(lsb0, f) + } +} + +impl<A:IterBytes> IterBytes for @[A] { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + borrow(*self).iter_bytes(lsb0, f) + } +} + +// NOTE: remove all of these after a snapshot, the new for-loop iteration +// protocol makes these unnecessary. + +#[inline(always)] +pub fn iter_bytes_2<A:IterBytes,B:IterBytes>(a: &A, b: &B, + lsb0: bool, z: Cb) -> bool { + a.iter_bytes(lsb0, z) && b.iter_bytes(lsb0, z) +} + +pub fn iter_bytes_3<A: IterBytes, + B: IterBytes, + C: IterBytes>(a: &A, b: &B, c: &C, lsb0: bool, z: Cb) -> bool { + a.iter_bytes(lsb0, z) && b.iter_bytes(lsb0, z) && c.iter_bytes(lsb0, z) +} + +pub fn iter_bytes_4<A: IterBytes, + B: IterBytes, + C: IterBytes, + D: IterBytes>(a: &A, b: &B, c: &C, + d: &D, + lsb0: bool, z: Cb) -> bool { + a.iter_bytes(lsb0, z) && b.iter_bytes(lsb0, z) && c.iter_bytes(lsb0, z) && + d.iter_bytes(lsb0, z) +} + +pub fn iter_bytes_5<A: IterBytes, + B: IterBytes, + C: IterBytes, + D: IterBytes, + E: IterBytes>(a: &A, b: &B, c: &C, + d: &D, e: &E, + lsb0: bool, z: Cb) -> bool { + a.iter_bytes(lsb0, z) && b.iter_bytes(lsb0, z) && c.iter_bytes(lsb0, z) && + d.iter_bytes(lsb0, z) && e.iter_bytes(lsb0, z) +} + +impl<'self> IterBytes for &'self str { + #[inline(always)] + fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool { + do str::byte_slice(*self) |bytes| { + f(bytes) + } + } +} + +impl IterBytes for ~str { + #[inline(always)] + fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool { + do str::byte_slice(*self) |bytes| { + f(bytes) + } + } +} + +impl IterBytes for @str { + #[inline(always)] + fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool { + do str::byte_slice(*self) |bytes| { + f(bytes) + } + } +} + +impl<A:IterBytes> IterBytes for Option<A> { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + match *self { + Some(ref a) => 0u8.iter_bytes(lsb0, f) && a.iter_bytes(lsb0, f), + None => 1u8.iter_bytes(lsb0, f) + } + } +} + +impl<'self,A:IterBytes> IterBytes for &'self A { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (**self).iter_bytes(lsb0, f) + } +} + +impl<A:IterBytes> IterBytes for @A { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (**self).iter_bytes(lsb0, f) + } +} + +impl<A:IterBytes> IterBytes for ~A { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (**self).iter_bytes(lsb0, f) + } +} + +// NB: raw-pointer IterBytes does _not_ dereference +// to the target; it just gives you the pointer-bytes. +impl<A> IterBytes for *const A { + #[inline(always)] + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + (*self as uint).iter_bytes(lsb0, f) + } +} + +pub trait ToBytes { + fn to_bytes(&self, lsb0: bool) -> ~[u8]; +} + +impl<A:IterBytes> ToBytes for A { + fn to_bytes(&self, lsb0: bool) -> ~[u8] { + do io::with_bytes_writer |wr| { + for self.iter_bytes(lsb0) |bytes| { + wr.write(bytes) + } + } + } +} diff --git a/src/libstd/to_str.rs b/src/libstd/to_str.rs new file mode 100644 index 00000000000..1469471b7ce --- /dev/null +++ b/src/libstd/to_str.rs @@ -0,0 +1,240 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +The `ToStr` trait for converting to strings + +*/ + +use str::OwnedStr; +use hashmap::HashMap; +use hashmap::HashSet; +use container::Map; +use hash::Hash; +use cmp::Eq; +use old_iter::BaseIter; + +pub trait ToStr { + fn to_str(&self) -> ~str; +} + +/// Trait for converting a type to a string, consuming it in the process. +pub trait ToStrConsume { + // Cosume and convert to a string. + fn to_str_consume(self) -> ~str; +} + +impl ToStr for bool { + #[inline(always)] + fn to_str(&self) -> ~str { ::bool::to_str(*self) } +} +impl ToStr for () { + #[inline(always)] + fn to_str(&self) -> ~str { ~"()" } +} + +impl<A:ToStr> ToStr for (A,) { + #[inline(always)] + fn to_str(&self) -> ~str { + match *self { + (ref a,) => { + ~"(" + a.to_str() + ~", " + ~")" + } + } + } +} + +impl<A:ToStr+Hash+Eq, B:ToStr+Hash+Eq> ToStr for HashMap<A, B> { + #[inline(always)] + fn to_str(&self) -> ~str { + let mut acc = ~"{", first = true; + for self.each |key, value| { + if first { + first = false; + } + else { + acc.push_str(", "); + } + acc.push_str(key.to_str()); + acc.push_str(": "); + acc.push_str(value.to_str()); + } + acc.push_char('}'); + acc + } +} + +impl<A:ToStr+Hash+Eq> ToStr for HashSet<A> { + #[inline(always)] + fn to_str(&self) -> ~str { + let mut acc = ~"{", first = true; + for self.each |element| { + if first { + first = false; + } + else { + acc.push_str(", "); + } + acc.push_str(element.to_str()); + } + acc.push_char('}'); + acc + } +} + +impl<A:ToStr,B:ToStr> ToStr for (A, B) { + #[inline(always)] + fn to_str(&self) -> ~str { + // FIXME(#4760): this causes an llvm assertion + //let &(ref a, ref b) = self; + match *self { + (ref a, ref b) => { + ~"(" + a.to_str() + ~", " + b.to_str() + ~")" + } + } + } +} + +impl<A:ToStr,B:ToStr,C:ToStr> ToStr for (A, B, C) { + #[inline(always)] + fn to_str(&self) -> ~str { + // FIXME(#4760): this causes an llvm assertion + //let &(ref a, ref b, ref c) = self; + match *self { + (ref a, ref b, ref c) => { + fmt!("(%s, %s, %s)", + (*a).to_str(), + (*b).to_str(), + (*c).to_str() + ) + } + } + } +} + +impl<'self,A:ToStr> ToStr for &'self [A] { + #[inline(always)] + fn to_str(&self) -> ~str { + let mut acc = ~"[", first = true; + for self.each |elt| { + if first { + first = false; + } + else { + acc.push_str(", "); + } + acc.push_str(elt.to_str()); + } + acc.push_char(']'); + acc + } +} + +impl<A:ToStr> ToStr for ~[A] { + #[inline(always)] + fn to_str(&self) -> ~str { + let mut acc = ~"[", first = true; + for self.each |elt| { + if first { + first = false; + } + else { + acc.push_str(", "); + } + acc.push_str(elt.to_str()); + } + acc.push_char(']'); + acc + } +} + +impl<A:ToStr> ToStr for @[A] { + #[inline(always)] + fn to_str(&self) -> ~str { + let mut acc = ~"[", first = true; + for self.each |elt| { + if first { + first = false; + } + else { + acc.push_str(", "); + } + acc.push_str(elt.to_str()); + } + acc.push_char(']'); + acc + } +} + +#[cfg(test)] +#[allow(non_implicitly_copyable_typarams)] +mod tests { + use hashmap::HashMap; + use hashmap::HashSet; + use container::Set; + #[test] + fn test_simple_types() { + assert_eq!(1i.to_str(), ~"1"); + assert_eq!((-1i).to_str(), ~"-1"); + assert_eq!(200u.to_str(), ~"200"); + assert_eq!(2u8.to_str(), ~"2"); + assert_eq!(true.to_str(), ~"true"); + assert_eq!(false.to_str(), ~"false"); + assert_eq!(().to_str(), ~"()"); + assert_eq!((~"hi").to_str(), ~"hi"); + assert_eq!((@"hi").to_str(), ~"hi"); + } + + #[test] + fn test_tuple_types() { + assert_eq!((1, 2).to_str(), ~"(1, 2)"); + assert_eq!((~"a", ~"b", false).to_str(), ~"(a, b, false)"); + assert_eq!(((), ((), 100)).to_str(), ~"((), ((), 100))"); + } + + #[test] + fn test_vectors() { + let x: ~[int] = ~[]; + assert_eq!(x.to_str(), ~"[]"); + assert_eq!((~[1]).to_str(), ~"[1]"); + assert_eq!((~[1, 2, 3]).to_str(), ~"[1, 2, 3]"); + assert!((~[~[], ~[1], ~[1, 1]]).to_str() == + ~"[[], [1], [1, 1]]"); + } + + #[test] + fn test_hashmap() { + let mut table: HashMap<int, int> = HashMap::new(); + let empty: HashMap<int, int> = HashMap::new(); + + table.insert(3, 4); + table.insert(1, 2); + + let table_str = table.to_str(); + + assert!(table_str == ~"{1: 2, 3: 4}" || table_str == ~"{3: 4, 1: 2}"); + assert_eq!(empty.to_str(), ~"{}"); + } + + #[test] + fn test_hashset() { + let mut set: HashSet<int> = HashSet::new(); + let empty_set: HashSet<int> = HashSet::new(); + + set.insert(1); + set.insert(2); + + let set_str = set.to_str(); + + assert!(set_str == ~"{1, 2}" || set_str == ~"{2, 1}"); + assert_eq!(empty_set.to_str(), ~"{}"); + } +} diff --git a/src/libstd/treemap.rs b/src/libstd/treemap.rs deleted file mode 100644 index 93f8d06ee08..00000000000 --- a/src/libstd/treemap.rs +++ /dev/null @@ -1,1240 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! An ordered map and set implemented as self-balancing binary search -//! trees. The only requirement for the types is that the key implements -//! `TotalOrd`. - -use core::iterator::*; -use core::util::{swap, replace}; - -// This is implemented as an AA tree, which is a simplified variation of -// a red-black tree where where red (horizontal) nodes can only be added -// as a right child. The time complexity is the same, and re-balancing -// operations are more frequent but also cheaper. - -// Future improvements: - -// range search - O(log n) retrieval of an iterator from some key - -// (possibly) implement the overloads Python does for sets: -// * intersection: & -// * difference: - -// * symmetric difference: ^ -// * union: | -// These would be convenient since the methods work like `each` - -pub struct TreeMap<K, V> { - priv root: Option<~TreeNode<K, V>>, - priv length: uint -} - -impl<K: Eq + TotalOrd, V: Eq> Eq for TreeMap<K, V> { - fn eq(&self, other: &TreeMap<K, V>) -> bool { - if self.len() != other.len() { - false - } else { - let mut x = self.iter(); - let mut y = other.iter(); - for self.len().times { - if x.next().unwrap() != y.next().unwrap() { - return false - } - } - true - } - } - fn ne(&self, other: &TreeMap<K, V>) -> bool { !self.eq(other) } -} - -// Lexicographical comparison -fn lt<K: Ord + TotalOrd, V>(a: &TreeMap<K, V>, - b: &TreeMap<K, V>) -> bool { - let mut x = a.iter(); - let mut y = b.iter(); - - let (a_len, b_len) = (a.len(), b.len()); - for uint::min(a_len, b_len).times { - let (key_a,_) = x.next().unwrap(); - let (key_b,_) = y.next().unwrap(); - if *key_a < *key_b { return true; } - if *key_a > *key_b { return false; } - }; - - a_len < b_len -} - -impl<K: Ord + TotalOrd, V> Ord for TreeMap<K, V> { - #[inline(always)] - fn lt(&self, other: &TreeMap<K, V>) -> bool { lt(self, other) } - #[inline(always)] - fn le(&self, other: &TreeMap<K, V>) -> bool { !lt(other, self) } - #[inline(always)] - fn ge(&self, other: &TreeMap<K, V>) -> bool { !lt(self, other) } - #[inline(always)] - fn gt(&self, other: &TreeMap<K, V>) -> bool { lt(other, self) } -} - -impl<K: TotalOrd, V> Container for TreeMap<K, V> { - /// Return the number of elements in the map - fn len(&const self) -> uint { self.length } - - /// Return true if the map contains no elements - fn is_empty(&const self) -> bool { self.root.is_none() } -} - -impl<K: TotalOrd, V> Mutable for TreeMap<K, V> { - /// Clear the map, removing all key-value pairs. - fn clear(&mut self) { - self.root = None; - self.length = 0 - } -} - -impl<K: TotalOrd, V> Map<K, V> for TreeMap<K, V> { - /// Return true if the map contains a value for the specified key - fn contains_key(&self, key: &K) -> bool { - self.find(key).is_some() - } - - /// Visit all key-value pairs in order - fn each<'a>(&'a self, f: &fn(&'a K, &'a V) -> bool) -> bool { - each(&self.root, f) - } - - /// Visit all keys in order - fn each_key(&self, f: &fn(&K) -> bool) -> bool { - self.each(|k, _| f(k)) - } - - /// Visit all values in order - fn each_value<'a>(&'a self, f: &fn(&'a V) -> bool) -> bool { - self.each(|_, v| f(v)) - } - - /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, f: &fn(&K, &mut V) -> bool) -> bool { - mutate_values(&mut self.root, f) - } - - /// Return a reference to the value corresponding to the key - fn find<'a>(&'a self, key: &K) -> Option<&'a V> { - let mut current: &'a Option<~TreeNode<K, V>> = &self.root; - loop { - match *current { - Some(ref r) => { - match key.cmp(&r.key) { - Less => current = &r.left, - Greater => current = &r.right, - Equal => return Some(&r.value) - } - } - None => return None - } - } - } - - /// Return a mutable reference to the value corresponding to the key - #[inline(always)] - fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V> { - find_mut(&mut self.root, key) - } - - /// Insert a key-value pair into the map. An existing value for a - /// key is replaced by the new value. Return true if the key did - /// not already exist in the map. - fn insert(&mut self, key: K, value: V) -> bool { - self.swap(key, value).is_none() - } - - /// Remove a key-value pair from the map. Return true if the key - /// was present in the map, otherwise false. - fn remove(&mut self, key: &K) -> bool { - self.pop(key).is_some() - } - - /// Insert a key-value pair from the map. If the key already had a value - /// present in the map, that value is returned. Otherwise None is returned. - fn swap(&mut self, key: K, value: V) -> Option<V> { - let ret = insert(&mut self.root, key, value); - if ret.is_none() { self.length += 1 } - ret - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - fn pop(&mut self, key: &K) -> Option<V> { - let ret = remove(&mut self.root, key); - if ret.is_some() { self.length -= 1 } - ret - } -} - -pub impl<K: TotalOrd, V> TreeMap<K, V> { - /// Create an empty TreeMap - fn new() -> TreeMap<K, V> { TreeMap{root: None, length: 0} } - - /// Visit all key-value pairs in reverse order - fn each_reverse<'a>(&'a self, f: &fn(&'a K, &'a V) -> bool) -> bool { - each_reverse(&self.root, f) - } - - /// Visit all keys in reverse order - fn each_key_reverse(&self, f: &fn(&K) -> bool) -> bool { - self.each_reverse(|k, _| f(k)) - } - - /// Visit all values in reverse order - fn each_value_reverse(&self, f: &fn(&V) -> bool) -> bool { - self.each_reverse(|_, v| f(v)) - } - - /// Get a lazy iterator over the key-value pairs in the map. - /// Requires that it be frozen (immutable). - fn iter<'a>(&'a self) -> TreeMapIterator<'a, K, V> { - TreeMapIterator{stack: ~[], node: &self.root} - } -} - -/// Lazy forward iterator over a map -pub struct TreeMapIterator<'self, K, V> { - priv stack: ~[&'self ~TreeNode<K, V>], - priv node: &'self Option<~TreeNode<K, V>> -} - -impl<'self, K, V> Iterator<(&'self K, &'self V)> for TreeMapIterator<'self, K, V> { - /// Advance the iterator to the next node (in order) and return a - /// tuple with a reference to the key and value. If there are no - /// more nodes, return `None`. - fn next(&mut self) -> Option<(&'self K, &'self V)> { - while !self.stack.is_empty() || self.node.is_some() { - match *self.node { - Some(ref x) => { - self.stack.push(x); - self.node = &x.left; - } - None => { - let res = self.stack.pop(); - self.node = &res.right; - return Some((&res.key, &res.value)); - } - } - } - None - } -} - -impl<'self, T> Iterator<&'self T> for TreeSetIterator<'self, T> { - /// Advance the iterator to the next node (in order). If there are no more nodes, return `None`. - #[inline(always)] - fn next(&mut self) -> Option<&'self T> { - do self.iter.next().map |&(value, _)| { value } - } -} - -pub struct TreeSet<T> { - priv map: TreeMap<T, ()> -} - -impl<T: TotalOrd> BaseIter<T> for TreeSet<T> { - /// Visit all values in order - #[inline(always)] - fn each(&self, f: &fn(&T) -> bool) -> bool { self.map.each_key(f) } - #[inline(always)] - fn size_hint(&self) -> Option<uint> { Some(self.len()) } -} - -impl<T: TotalOrd> ReverseIter<T> for TreeSet<T> { - /// Visit all values in reverse order - #[inline(always)] - fn each_reverse(&self, f: &fn(&T) -> bool) -> bool { - self.map.each_key_reverse(f) - } -} - -impl<T: Eq + TotalOrd> Eq for TreeSet<T> { - #[inline(always)] - fn eq(&self, other: &TreeSet<T>) -> bool { self.map == other.map } - #[inline(always)] - fn ne(&self, other: &TreeSet<T>) -> bool { self.map != other.map } -} - -impl<T: Ord + TotalOrd> Ord for TreeSet<T> { - #[inline(always)] - fn lt(&self, other: &TreeSet<T>) -> bool { self.map < other.map } - #[inline(always)] - fn le(&self, other: &TreeSet<T>) -> bool { self.map <= other.map } - #[inline(always)] - fn ge(&self, other: &TreeSet<T>) -> bool { self.map >= other.map } - #[inline(always)] - fn gt(&self, other: &TreeSet<T>) -> bool { self.map > other.map } -} - -impl<T: TotalOrd> Container for TreeSet<T> { - /// Return the number of elements in the set - #[inline(always)] - fn len(&const self) -> uint { self.map.len() } - - /// Return true if the set contains no elements - #[inline(always)] - fn is_empty(&const self) -> bool { self.map.is_empty() } -} - -impl<T: TotalOrd> Mutable for TreeSet<T> { - /// Clear the set, removing all values. - #[inline(always)] - fn clear(&mut self) { self.map.clear() } -} - -impl<T: TotalOrd> Set<T> for TreeSet<T> { - /// Return true if the set contains a value - #[inline(always)] - fn contains(&self, value: &T) -> bool { - self.map.contains_key(value) - } - - /// Add a value to the set. Return true if the value was not already - /// present in the set. - #[inline(always)] - fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()) } - - /// Remove a value from the set. Return true if the value was - /// present in the set. - #[inline(always)] - fn remove(&mut self, value: &T) -> bool { self.map.remove(value) } - - /// Return true if the set has no elements in common with `other`. - /// This is equivalent to checking for an empty intersection. - fn is_disjoint(&self, other: &TreeSet<T>) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - let mut a = x.next(); - let mut b = y.next(); - while a.is_some() && b.is_some() { - let a1 = a.unwrap(); - let b1 = b.unwrap(); - match a1.cmp(b1) { - Less => a = x.next(), - Greater => b = y.next(), - Equal => return false - } - } - true - } - - /// Return true if the set is a subset of another - #[inline(always)] - fn is_subset(&self, other: &TreeSet<T>) -> bool { - other.is_superset(self) - } - - /// Return true if the set is a superset of another - fn is_superset(&self, other: &TreeSet<T>) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - let mut a = x.next(); - let mut b = y.next(); - while b.is_some() { - if a.is_none() { - return false - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - match a1.cmp(b1) { - Less => (), - Greater => return false, - Equal => b = y.next(), - } - - a = x.next(); - } - true - } - - /// Visit the values (in-order) representing the difference - fn difference(&self, other: &TreeSet<T>, f: &fn(&T) -> bool) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - - let mut a = x.next(); - let mut b = y.next(); - - while a.is_some() { - if b.is_none() { - return f(a.unwrap()) && x.advance(f); - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - let cmp = a1.cmp(b1); - - if cmp == Less { - if !f(a1) { return false; } - a = x.next(); - } else { - if cmp == Equal { a = x.next() } - b = y.next(); - } - } - return true; - } - - /// Visit the values (in-order) representing the symmetric difference - fn symmetric_difference(&self, other: &TreeSet<T>, - f: &fn(&T) -> bool) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - - let mut a = x.next(); - let mut b = y.next(); - - while a.is_some() { - if b.is_none() { - return f(a.unwrap()) && x.advance(f); - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - let cmp = a1.cmp(b1); - - if cmp == Less { - if !f(a1) { return false; } - a = x.next(); - } else { - if cmp == Greater { - if !f(b1) { return false; } - } else { - a = x.next(); - } - b = y.next(); - } - } - return b.each(|&x| f(x)) && y.advance(f); - } - - /// Visit the values (in-order) representing the intersection - fn intersection(&self, other: &TreeSet<T>, f: &fn(&T) -> bool) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - - let mut a = x.next(); - let mut b = y.next(); - - while a.is_some() && b.is_some() { - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - let cmp = a1.cmp(b1); - - if cmp == Less { - a = x.next(); - } else { - if cmp == Equal { - if !f(a1) { return false } - } - b = y.next(); - } - } - return true; - } - - /// Visit the values (in-order) representing the union - fn union(&self, other: &TreeSet<T>, f: &fn(&T) -> bool) -> bool { - let mut x = self.iter(); - let mut y = other.iter(); - - let mut a = x.next(); - let mut b = y.next(); - - while a.is_some() { - if b.is_none() { - return f(a.unwrap()) && x.advance(f); - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - let cmp = a1.cmp(b1); - - if cmp == Greater { - if !f(b1) { return false; } - b = y.next(); - } else { - if !f(a1) { return false; } - if cmp == Equal { - b = y.next(); - } - a = x.next(); - } - } - return b.each(|&x| f(x)) && y.advance(f); - } -} - -pub impl <T: TotalOrd> TreeSet<T> { - /// Create an empty TreeSet - #[inline(always)] - fn new() -> TreeSet<T> { TreeSet{map: TreeMap::new()} } - - /// Get a lazy iterator over the values in the set. - /// Requires that it be frozen (immutable). - #[inline(always)] - fn iter<'a>(&'a self) -> TreeSetIterator<'a, T> { - TreeSetIterator{iter: self.map.iter()} - } -} - -/// Lazy forward iterator over a set -pub struct TreeSetIterator<'self, T> { - priv iter: TreeMapIterator<'self, T, ()> -} - -// Nodes keep track of their level in the tree, starting at 1 in the -// leaves and with a red child sharing the level of the parent. -struct TreeNode<K, V> { - key: K, - value: V, - left: Option<~TreeNode<K, V>>, - right: Option<~TreeNode<K, V>>, - level: uint -} - -pub impl<K: TotalOrd, V> TreeNode<K, V> { - #[inline(always)] - fn new(key: K, value: V) -> TreeNode<K, V> { - TreeNode{key: key, value: value, left: None, right: None, level: 1} - } -} - -fn each<'r, K: TotalOrd, V>(node: &'r Option<~TreeNode<K, V>>, - f: &fn(&'r K, &'r V) -> bool) -> bool { - node.each(|x| each(&x.left, f) && f(&x.key, &x.value) && - each(&x.right, f)) -} - -fn each_reverse<'r, K: TotalOrd, V>(node: &'r Option<~TreeNode<K, V>>, - f: &fn(&'r K, &'r V) -> bool) -> bool { - node.each(|x| each_reverse(&x.right, f) && f(&x.key, &x.value) && - each_reverse(&x.left, f)) -} - -fn mutate_values<'r, K: TotalOrd, V>(node: &'r mut Option<~TreeNode<K, V>>, - f: &fn(&'r K, &'r mut V) -> bool) - -> bool { - match *node { - Some(~TreeNode{key: ref key, value: ref mut value, left: ref mut left, - right: ref mut right, _}) => { - if !mutate_values(left, f) { return false } - if !f(key, value) { return false } - if !mutate_values(right, f) { return false } - } - None => return false - } - true -} - -// Remove left horizontal link by rotating right -fn skew<K: TotalOrd, V>(node: &mut ~TreeNode<K, V>) { - if node.left.map_default(false, |x| x.level == node.level) { - let mut save = node.left.swap_unwrap(); - swap(&mut node.left, &mut save.right); // save.right now None - swap(node, &mut save); - node.right = Some(save); - } -} - -// Remove dual horizontal link by rotating left and increasing level of -// the parent -fn split<K: TotalOrd, V>(node: &mut ~TreeNode<K, V>) { - if node.right.map_default(false, - |x| x.right.map_default(false, |y| y.level == node.level)) { - let mut save = node.right.swap_unwrap(); - swap(&mut node.right, &mut save.left); // save.left now None - save.level += 1; - swap(node, &mut save); - node.left = Some(save); - } -} - -fn find_mut<'r, K: TotalOrd, V>(node: &'r mut Option<~TreeNode<K, V>>, - key: &K) - -> Option<&'r mut V> { - match *node { - Some(ref mut x) => { - match key.cmp(&x.key) { - Less => find_mut(&mut x.left, key), - Greater => find_mut(&mut x.right, key), - Equal => Some(&mut x.value), - } - } - None => None - } -} - -fn insert<K: TotalOrd, V>(node: &mut Option<~TreeNode<K, V>>, - key: K, value: V) -> Option<V> { - match *node { - Some(ref mut save) => { - match key.cmp(&save.key) { - Less => { - let inserted = insert(&mut save.left, key, value); - skew(save); - split(save); - inserted - } - Greater => { - let inserted = insert(&mut save.right, key, value); - skew(save); - split(save); - inserted - } - Equal => { - save.key = key; - Some(replace(&mut save.value, value)) - } - } - } - None => { - *node = Some(~TreeNode::new(key, value)); - None - } - } -} - -fn remove<K: TotalOrd, V>(node: &mut Option<~TreeNode<K, V>>, - key: &K) -> Option<V> { - fn heir_swap<K: TotalOrd, V>(node: &mut ~TreeNode<K, V>, - child: &mut Option<~TreeNode<K, V>>) { - // *could* be done without recursion, but it won't borrow check - for child.each_mut |x| { - if x.right.is_some() { - heir_swap(node, &mut x.right); - } else { - swap(&mut node.key, &mut x.key); - swap(&mut node.value, &mut x.value); - } - } - } - - match *node { - None => { - return None; // bottom of tree - } - Some(ref mut save) => { - let (ret, rebalance) = match key.cmp(&save.key) { - Less => (remove(&mut save.left, key), true), - Greater => (remove(&mut save.right, key), true), - Equal => { - if save.left.is_some() { - if save.right.is_some() { - let mut left = save.left.swap_unwrap(); - if left.right.is_some() { - heir_swap(save, &mut left.right); - } else { - swap(&mut save.key, &mut left.key); - swap(&mut save.value, &mut left.value); - } - save.left = Some(left); - (remove(&mut save.left, key), true) - } else { - let new = save.left.swap_unwrap(); - let ~TreeNode{value, _} = replace(save, new); - *save = save.left.swap_unwrap(); - (Some(value), true) - } - } else if save.right.is_some() { - let new = save.right.swap_unwrap(); - let ~TreeNode{value, _} = replace(save, new); - (Some(value), true) - } else { - (None, false) - } - } - }; - - if rebalance { - let left_level = save.left.map_default(0, |x| x.level); - let right_level = save.right.map_default(0, |x| x.level); - - // re-balance, if necessary - if left_level < save.level - 1 || right_level < save.level - 1 { - save.level -= 1; - - if right_level > save.level { - for save.right.each_mut |x| { x.level = save.level } - } - - skew(save); - - for save.right.each_mut |right| { - skew(right); - for right.right.each_mut |x| { skew(x) } - } - - split(save); - for save.right.each_mut |x| { split(x) } - } - - return ret; - } - } - } - return match replace(node, None) { - Some(~TreeNode{value, _}) => Some(value), None => fail!() - }; -} - -#[cfg(test)] -mod test_treemap { - use core::iterator::*; - use super::*; - use core::rand::RngUtil; - - #[test] - fn find_empty() { - let m = TreeMap::new::<int, int>(); assert!(m.find(&5) == None); - } - - #[test] - fn find_not_found() { - let mut m = TreeMap::new(); - assert!(m.insert(1, 2)); - assert!(m.insert(5, 3)); - assert!(m.insert(9, 3)); - assert_eq!(m.find(&2), None); - } - - #[test] - fn test_find_mut() { - let mut m = TreeMap::new(); - assert!(m.insert(1, 12)); - assert!(m.insert(2, 8)); - assert!(m.insert(5, 14)); - let new = 100; - match m.find_mut(&5) { - None => fail!(), Some(x) => *x = new - } - assert_eq!(m.find(&5), Some(&new)); - } - - #[test] - fn insert_replace() { - let mut m = TreeMap::new(); - assert!(m.insert(5, 2)); - assert!(m.insert(2, 9)); - assert!(!m.insert(2, 11)); - assert_eq!(m.find(&2).unwrap(), &11); - } - - #[test] - fn test_clear() { - let mut m = TreeMap::new(); - m.clear(); - assert!(m.insert(5, 11)); - assert!(m.insert(12, -3)); - assert!(m.insert(19, 2)); - m.clear(); - assert!(m.find(&5).is_none()); - assert!(m.find(&12).is_none()); - assert!(m.find(&19).is_none()); - assert!(m.is_empty()); - } - - #[test] - fn u8_map() { - let mut m = TreeMap::new(); - - let k1 = str::to_bytes(~"foo"); - let k2 = str::to_bytes(~"bar"); - let v1 = str::to_bytes(~"baz"); - let v2 = str::to_bytes(~"foobar"); - - m.insert(copy k1, copy v1); - m.insert(copy k2, copy v2); - - assert_eq!(m.find(&k2), Some(&v2)); - assert_eq!(m.find(&k1), Some(&v1)); - } - - fn check_equal<K: Eq + TotalOrd, V: Eq>(ctrl: &[(K, V)], - map: &TreeMap<K, V>) { - assert_eq!(ctrl.is_empty(), map.is_empty()); - for ctrl.each |x| { - let &(k, v) = x; - assert!(map.find(&k).unwrap() == &v) - } - for map.each |map_k, map_v| { - let mut found = false; - for ctrl.each |x| { - let &(ctrl_k, ctrl_v) = x; - if *map_k == ctrl_k { - assert!(*map_v == ctrl_v); - found = true; - break; - } - } - assert!(found); - } - } - - fn check_left<K: TotalOrd, V>(node: &Option<~TreeNode<K, V>>, - parent: &~TreeNode<K, V>) { - match *node { - Some(ref r) => { - assert_eq!(r.key.cmp(&parent.key), Less); - assert!(r.level == parent.level - 1); // left is black - check_left(&r.left, r); - check_right(&r.right, r, false); - } - None => assert!(parent.level == 1) // parent is leaf - } - } - - fn check_right<K: TotalOrd, V>(node: &Option<~TreeNode<K, V>>, - parent: &~TreeNode<K, V>, - parent_red: bool) { - match *node { - Some(ref r) => { - assert_eq!(r.key.cmp(&parent.key), Greater); - let red = r.level == parent.level; - if parent_red { assert!(!red) } // no dual horizontal links - // Right red or black - assert!(red || r.level == parent.level - 1); - check_left(&r.left, r); - check_right(&r.right, r, red); - } - None => assert!(parent.level == 1) // parent is leaf - } - } - - fn check_structure<K: TotalOrd, V>(map: &TreeMap<K, V>) { - match map.root { - Some(ref r) => { - check_left(&r.left, r); - check_right(&r.right, r, false); - } - None => () - } - } - - #[test] - fn test_rand_int() { - let mut map = TreeMap::new::<int, int>(); - let mut ctrl = ~[]; - - check_equal(ctrl, &map); - assert!(map.find(&5).is_none()); - - let mut rng = rand::IsaacRng::new_seeded(&[42]); - - for 3.times { - for 90.times { - let k = rng.gen(); - let v = rng.gen(); - if !ctrl.contains(&(k, v)) { - assert!(map.insert(k, v)); - ctrl.push((k, v)); - check_structure(&map); - check_equal(ctrl, &map); - } - } - - for 30.times { - let r = rng.gen_uint_range(0, ctrl.len()); - let (key, _) = vec::remove(&mut ctrl, r); - assert!(map.remove(&key)); - check_structure(&map); - check_equal(ctrl, &map); - } - } - } - - #[test] - fn test_len() { - let mut m = TreeMap::new(); - assert!(m.insert(3, 6)); - assert_eq!(m.len(), 1); - assert!(m.insert(0, 0)); - assert_eq!(m.len(), 2); - assert!(m.insert(4, 8)); - assert_eq!(m.len(), 3); - assert!(m.remove(&3)); - assert_eq!(m.len(), 2); - assert!(!m.remove(&5)); - assert_eq!(m.len(), 2); - assert!(m.insert(2, 4)); - assert_eq!(m.len(), 3); - assert!(m.insert(1, 2)); - assert_eq!(m.len(), 4); - } - - #[test] - fn test_each() { - let mut m = TreeMap::new(); - - assert!(m.insert(3, 6)); - assert!(m.insert(0, 0)); - assert!(m.insert(4, 8)); - assert!(m.insert(2, 4)); - assert!(m.insert(1, 2)); - - let mut n = 0; - for m.each |k, v| { - assert_eq!(*k, n); - assert_eq!(*v, n * 2); - n += 1; - } - } - - #[test] - fn test_each_reverse() { - let mut m = TreeMap::new(); - - assert!(m.insert(3, 6)); - assert!(m.insert(0, 0)); - assert!(m.insert(4, 8)); - assert!(m.insert(2, 4)); - assert!(m.insert(1, 2)); - - let mut n = 4; - for m.each_reverse |k, v| { - assert_eq!(*k, n); - assert_eq!(*v, n * 2); - n -= 1; - } - } - - #[test] - fn test_eq() { - let mut a = TreeMap::new(); - let mut b = TreeMap::new(); - - assert!(a == b); - assert!(a.insert(0, 5)); - assert!(a != b); - assert!(b.insert(0, 4)); - assert!(a != b); - assert!(a.insert(5, 19)); - assert!(a != b); - assert!(!b.insert(0, 5)); - assert!(a != b); - assert!(b.insert(5, 19)); - assert!(a == b); - } - - #[test] - fn test_lt() { - let mut a = TreeMap::new(); - let mut b = TreeMap::new(); - - assert!(!(a < b) && !(b < a)); - assert!(b.insert(0, 5)); - assert!(a < b); - assert!(a.insert(0, 7)); - assert!(!(a < b) && !(b < a)); - assert!(b.insert(-2, 0)); - assert!(b < a); - assert!(a.insert(-5, 2)); - assert!(a < b); - assert!(a.insert(6, 2)); - assert!(a < b && !(b < a)); - } - - #[test] - fn test_ord() { - let mut a = TreeMap::new(); - let mut b = TreeMap::new(); - - assert!(a <= b && a >= b); - assert!(a.insert(1, 1)); - assert!(a > b && a >= b); - assert!(b < a && b <= a); - assert!(b.insert(2, 2)); - assert!(b > a && b >= a); - assert!(a < b && a <= b); - } - - #[test] - fn test_lazy_iterator() { - let mut m = TreeMap::new(); - let (x1, y1) = (2, 5); - let (x2, y2) = (9, 12); - let (x3, y3) = (20, -3); - let (x4, y4) = (29, 5); - let (x5, y5) = (103, 3); - - assert!(m.insert(x1, y1)); - assert!(m.insert(x2, y2)); - assert!(m.insert(x3, y3)); - assert!(m.insert(x4, y4)); - assert!(m.insert(x5, y5)); - - let m = m; - let mut a = m.iter(); - - assert_eq!(a.next().unwrap(), (&x1, &y1)); - assert_eq!(a.next().unwrap(), (&x2, &y2)); - assert_eq!(a.next().unwrap(), (&x3, &y3)); - assert_eq!(a.next().unwrap(), (&x4, &y4)); - assert_eq!(a.next().unwrap(), (&x5, &y5)); - - assert!(a.next().is_none()); - - let mut b = m.iter(); - - let expected = [(&x1, &y1), (&x2, &y2), (&x3, &y3), (&x4, &y4), - (&x5, &y5)]; - let mut i = 0; - - for b.advance |x| { - assert_eq!(expected[i], x); - i += 1; - - if i == 2 { - break - } - } - - for b.advance |x| { - assert_eq!(expected[i], x); - i += 1; - } - } -} - -#[cfg(test)] -mod test_set { - use core::iterator::*; - use super::*; - - #[test] - fn test_clear() { - let mut s = TreeSet::new(); - s.clear(); - assert!(s.insert(5)); - assert!(s.insert(12)); - assert!(s.insert(19)); - s.clear(); - assert!(!s.contains(&5)); - assert!(!s.contains(&12)); - assert!(!s.contains(&19)); - assert!(s.is_empty()); - } - - #[test] - fn test_disjoint() { - let mut xs = TreeSet::new(); - let mut ys = TreeSet::new(); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(5)); - assert!(ys.insert(11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(7)); - assert!(xs.insert(19)); - assert!(xs.insert(4)); - assert!(ys.insert(2)); - assert!(ys.insert(-11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(ys.insert(7)); - assert!(!xs.is_disjoint(&ys)); - assert!(!ys.is_disjoint(&xs)); - } - - #[test] - fn test_subset_and_superset() { - let mut a = TreeSet::new(); - assert!(a.insert(0)); - assert!(a.insert(5)); - assert!(a.insert(11)); - assert!(a.insert(7)); - - let mut b = TreeSet::new(); - assert!(b.insert(0)); - assert!(b.insert(7)); - assert!(b.insert(19)); - assert!(b.insert(250)); - assert!(b.insert(11)); - assert!(b.insert(200)); - - assert!(!a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(!b.is_superset(&a)); - - assert!(b.insert(5)); - - assert!(a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(b.is_superset(&a)); - } - - #[test] - fn test_each() { - let mut m = TreeSet::new(); - - assert!(m.insert(3)); - assert!(m.insert(0)); - assert!(m.insert(4)); - assert!(m.insert(2)); - assert!(m.insert(1)); - - let mut n = 0; - for m.each |x| { - assert_eq!(*x, n); - n += 1 - } - } - - #[test] - fn test_each_reverse() { - let mut m = TreeSet::new(); - - assert!(m.insert(3)); - assert!(m.insert(0)); - assert!(m.insert(4)); - assert!(m.insert(2)); - assert!(m.insert(1)); - - let mut n = 4; - for m.each_reverse |x| { - assert_eq!(*x, n); - n -= 1 - } - } - - fn check(a: &[int], b: &[int], expected: &[int], - f: &fn(&TreeSet<int>, &TreeSet<int>, f: &fn(&int) -> bool) -> bool) { - let mut set_a = TreeSet::new(); - let mut set_b = TreeSet::new(); - - for a.each |x| { assert!(set_a.insert(*x)) } - for b.each |y| { assert!(set_b.insert(*y)) } - - let mut i = 0; - for f(&set_a, &set_b) |x| { - assert_eq!(*x, expected[i]); - i += 1; - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_intersection() { - fn check_intersection(a: &[int], b: &[int], expected: &[int]) { - check(a, b, expected, |x, y, z| x.intersection(y, z)) - } - - check_intersection([], [], []); - check_intersection([1, 2, 3], [], []); - check_intersection([], [1, 2, 3], []); - check_intersection([2], [1, 2, 3], [2]); - check_intersection([1, 2, 3], [2], [2]); - check_intersection([11, 1, 3, 77, 103, 5, -5], - [2, 11, 77, -9, -42, 5, 3], - [3, 5, 11, 77]); - } - - #[test] - fn test_difference() { - fn check_difference(a: &[int], b: &[int], expected: &[int]) { - check(a, b, expected, |x, y, z| x.difference(y, z)) - } - - check_difference([], [], []); - check_difference([1, 12], [], [1, 12]); - check_difference([], [1, 2, 3, 9], []); - check_difference([1, 3, 5, 9, 11], - [3, 9], - [1, 5, 11]); - check_difference([-5, 11, 22, 33, 40, 42], - [-12, -5, 14, 23, 34, 38, 39, 50], - [11, 22, 33, 40, 42]); - } - - #[test] - fn test_symmetric_difference() { - fn check_symmetric_difference(a: &[int], b: &[int], - expected: &[int]) { - check(a, b, expected, |x, y, z| x.symmetric_difference(y, z)) - } - - check_symmetric_difference([], [], []); - check_symmetric_difference([1, 2, 3], [2], [1, 3]); - check_symmetric_difference([2], [1, 2, 3], [1, 3]); - check_symmetric_difference([1, 3, 5, 9, 11], - [-2, 3, 9, 14, 22], - [-2, 1, 5, 11, 14, 22]); - } - - #[test] - fn test_union() { - fn check_union(a: &[int], b: &[int], - expected: &[int]) { - check(a, b, expected, |x, y, z| x.union(y, z)) - } - - check_union([], [], []); - check_union([1, 2, 3], [2], [1, 2, 3]); - check_union([2], [1, 2, 3], [1, 2, 3]); - check_union([1, 3, 5, 9, 11, 16, 19, 24], - [-2, 1, 5, 9, 13, 19], - [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24]); - } - - #[test] - fn test_zip() { - let mut x = TreeSet::new(); - x.insert(5u); - x.insert(12u); - x.insert(11u); - - let mut y = TreeSet::new(); - y.insert("foo"); - y.insert("bar"); - - let x = x; - let y = y; - let mut z = x.iter().zip(y.iter()); - - // FIXME: #5801: this needs a type hint to compile... - let result: Option<(&uint, & &'static str)> = z.next(); - assert_eq!(result.unwrap(), (&5u, & &"bar")); - - let result: Option<(&uint, & &'static str)> = z.next(); - assert_eq!(result.unwrap(), (&11u, & &"foo")); - - let result: Option<(&uint, & &'static str)> = z.next(); - assert!(result.is_none()); - } - - #[test] - fn test_swap() { - let mut m = TreeMap::new(); - assert_eq!(m.swap(1, 2), None); - assert_eq!(m.swap(1, 3), Some(2)); - assert_eq!(m.swap(1, 4), Some(3)); - } - - #[test] - fn test_pop() { - let mut m = TreeMap::new(); - m.insert(1, 2); - assert_eq!(m.pop(&1), Some(2)); - assert_eq!(m.pop(&1), None); - } -} diff --git a/src/libstd/trie.rs b/src/libstd/trie.rs new file mode 100644 index 00000000000..13b892e700e --- /dev/null +++ b/src/libstd/trie.rs @@ -0,0 +1,548 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! An ordered map and set for integer keys implemented as a radix trie + +use prelude::*; +use util::{swap, replace}; + +// FIXME: #5244: need to manually update the TrieNode constructor +static SHIFT: uint = 4; +static SIZE: uint = 1 << SHIFT; +static MASK: uint = SIZE - 1; + +enum Child<T> { + Internal(~TrieNode<T>), + External(uint, T), + Nothing +} + +pub struct TrieMap<T> { + priv root: TrieNode<T>, + priv length: uint +} + +impl<T> Container for TrieMap<T> { + /// Return the number of elements in the map + #[inline(always)] + fn len(&const self) -> uint { self.length } + + /// Return true if the map contains no elements + #[inline(always)] + fn is_empty(&const self) -> bool { self.len() == 0 } +} + +impl<T> Mutable for TrieMap<T> { + /// Clear the map, removing all values. + #[inline(always)] + fn clear(&mut self) { + self.root = TrieNode::new(); + self.length = 0; + } +} + +impl<T> Map<uint, T> for TrieMap<T> { + /// Return true if the map contains a value for the specified key + #[inline(always)] + fn contains_key(&self, key: &uint) -> bool { + self.find(key).is_some() + } + + /// Visit all key-value pairs in order + #[inline(always)] + fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + self.root.each(f) + } + + /// Visit all keys in order + #[inline(always)] + fn each_key(&self, f: &fn(&uint) -> bool) -> bool { + self.each(|k, _| f(k)) + } + + /// Visit all values in order + #[inline(always)] + fn each_value<'a>(&'a self, f: &fn(&'a T) -> bool) -> bool { + self.each(|_, v| f(v)) + } + + /// Iterate over the map and mutate the contained values + #[inline(always)] + fn mutate_values(&mut self, f: &fn(&uint, &mut T) -> bool) -> bool { + self.root.mutate_values(f) + } + + /// Return a reference to the value corresponding to the key + #[inline(hint)] + fn find<'a>(&'a self, key: &uint) -> Option<&'a T> { + let mut node: &'a TrieNode<T> = &self.root; + let mut idx = 0; + loop { + match node.children[chunk(*key, idx)] { + Internal(ref x) => node = &**x, + External(stored, ref value) => { + if stored == *key { + return Some(value) + } else { + return None + } + } + Nothing => return None + } + idx += 1; + } + } + + /// Return a mutable reference to the value corresponding to the key + #[inline(always)] + fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut T> { + find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) + } + + /// Insert a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Return true if the key did + /// not already exist in the map. + #[inline(always)] + fn insert(&mut self, key: uint, value: T) -> bool { + self.swap(key, value).is_none() + } + + /// Remove a key-value pair from the map. Return true if the key + /// was present in the map, otherwise false. + #[inline(always)] + fn remove(&mut self, key: &uint) -> bool { + self.pop(key).is_some() + } + + /// Insert a key-value pair from the map. If the key already had a value + /// present in the map, that value is returned. Otherwise None is returned. + fn swap(&mut self, key: uint, value: T) -> Option<T> { + let ret = insert(&mut self.root.count, + &mut self.root.children[chunk(key, 0)], + key, value, 1); + if ret.is_none() { self.length += 1 } + ret + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + fn pop(&mut self, key: &uint) -> Option<T> { + let ret = remove(&mut self.root.count, + &mut self.root.children[chunk(*key, 0)], + *key, 1); + if ret.is_some() { self.length -= 1 } + ret + } +} + +pub impl<T> TrieMap<T> { + /// Create an empty TrieMap + #[inline(always)] + fn new() -> TrieMap<T> { + TrieMap{root: TrieNode::new(), length: 0} + } + + /// Visit all key-value pairs in reverse order + #[inline(always)] + fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + self.root.each_reverse(f) + } + + /// Visit all keys in reverse order + #[inline(always)] + fn each_key_reverse(&self, f: &fn(&uint) -> bool) -> bool { + self.each_reverse(|k, _| f(k)) + } + + /// Visit all values in reverse order + #[inline(always)] + fn each_value_reverse(&self, f: &fn(&T) -> bool) -> bool { + self.each_reverse(|_, v| f(v)) + } +} + +pub struct TrieSet { + priv map: TrieMap<()> +} + +impl BaseIter<uint> for TrieSet { + /// Visit all values in order + #[inline(always)] + fn each(&self, f: &fn(&uint) -> bool) -> bool { self.map.each_key(f) } + #[inline(always)] + fn size_hint(&self) -> Option<uint> { Some(self.len()) } +} + +impl ReverseIter<uint> for TrieSet { + /// Visit all values in reverse order + #[inline(always)] + fn each_reverse(&self, f: &fn(&uint) -> bool) -> bool { + self.map.each_key_reverse(f) + } +} + +impl Container for TrieSet { + /// Return the number of elements in the set + #[inline(always)] + fn len(&const self) -> uint { self.map.len() } + + /// Return true if the set contains no elements + #[inline(always)] + fn is_empty(&const self) -> bool { self.map.is_empty() } +} + +impl Mutable for TrieSet { + /// Clear the set, removing all values. + #[inline(always)] + fn clear(&mut self) { self.map.clear() } +} + +pub impl TrieSet { + /// Create an empty TrieSet + #[inline(always)] + fn new() -> TrieSet { + TrieSet{map: TrieMap::new()} + } + + /// Return true if the set contains a value + #[inline(always)] + fn contains(&self, value: &uint) -> bool { + self.map.contains_key(value) + } + + /// Add a value to the set. Return true if the value was not already + /// present in the set. + #[inline(always)] + fn insert(&mut self, value: uint) -> bool { self.map.insert(value, ()) } + + /// Remove a value from the set. Return true if the value was + /// present in the set. + #[inline(always)] + fn remove(&mut self, value: &uint) -> bool { self.map.remove(value) } +} + +struct TrieNode<T> { + count: uint, + children: [Child<T>, ..SIZE] +} + +impl<T> TrieNode<T> { + #[inline(always)] + fn new() -> TrieNode<T> { + // FIXME: #5244: [Nothing, ..SIZE] should be possible without Copy + TrieNode{count: 0, + children: [Nothing, Nothing, Nothing, Nothing, + Nothing, Nothing, Nothing, Nothing, + Nothing, Nothing, Nothing, Nothing, + Nothing, Nothing, Nothing, Nothing]} + } +} + +impl<T> TrieNode<T> { + fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + for uint::range(0, self.children.len()) |idx| { + match self.children[idx] { + Internal(ref x) => if !x.each(f) { return false }, + External(k, ref v) => if !f(&k, v) { return false }, + Nothing => () + } + } + true + } + + fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + for uint::range_rev(self.children.len(), 0) |idx| { + match self.children[idx - 1] { + Internal(ref x) => if !x.each_reverse(f) { return false }, + External(k, ref v) => if !f(&k, v) { return false }, + Nothing => () + } + } + true + } + + fn mutate_values<'a>(&'a mut self, f: &fn(&uint, &mut T) -> bool) -> bool { + for vec::each_mut(self.children) |child| { + match *child { + Internal(ref mut x) => if !x.mutate_values(f) { + return false + }, + External(k, ref mut v) => if !f(&k, v) { return false }, + Nothing => () + } + } + true + } +} + +// if this was done via a trait, the key could be generic +#[inline(always)] +fn chunk(n: uint, idx: uint) -> uint { + let sh = uint::bits - (SHIFT * (idx + 1)); + (n >> sh) & MASK +} + +fn find_mut<'r, T>(child: &'r mut Child<T>, key: uint, idx: uint) -> Option<&'r mut T> { + match *child { + External(_, ref mut value) => Some(value), + Internal(ref mut x) => find_mut(&mut x.children[chunk(key, idx)], key, idx + 1), + Nothing => None + } +} + +fn insert<T>(count: &mut uint, child: &mut Child<T>, key: uint, value: T, + idx: uint) -> Option<T> { + let mut tmp = Nothing; + let ret; + swap(&mut tmp, child); + + *child = match tmp { + External(stored_key, stored_value) => { + if stored_key == key { + ret = Some(stored_value); + External(stored_key, value) + } else { + // conflict - split the node + let mut new = ~TrieNode::new(); + insert(&mut new.count, + &mut new.children[chunk(stored_key, idx)], + stored_key, stored_value, idx + 1); + ret = insert(&mut new.count, &mut new.children[chunk(key, idx)], + key, value, idx + 1); + Internal(new) + } + } + Internal(x) => { + let mut x = x; + ret = insert(&mut x.count, &mut x.children[chunk(key, idx)], key, + value, idx + 1); + Internal(x) + } + Nothing => { + *count += 1; + ret = None; + External(key, value) + } + }; + return ret; +} + +fn remove<T>(count: &mut uint, child: &mut Child<T>, key: uint, + idx: uint) -> Option<T> { + let (ret, this) = match *child { + External(stored, _) if stored == key => { + match replace(child, Nothing) { + External(_, value) => (Some(value), true), + _ => fail!() + } + } + External(*) => (None, false), + Internal(ref mut x) => { + let ret = remove(&mut x.count, &mut x.children[chunk(key, idx)], + key, idx + 1); + (ret, x.count == 0) + } + Nothing => (None, false) + }; + + if this { + *child = Nothing; + *count -= 1; + } + return ret; +} + +#[cfg(test)] +pub fn check_integrity<T>(trie: &TrieNode<T>) { + assert!(trie.count != 0); + + let mut sum = 0; + + for trie.children.each |x| { + match *x { + Nothing => (), + Internal(ref y) => { + check_integrity(&**y); + sum += 1 + } + External(_, _) => { sum += 1 } + } + } + + assert_eq!(sum, trie.count); +} + +#[cfg(test)] +mod tests { + use super::*; + use core::option::{Some, None}; + use uint; + + #[test] + fn test_find_mut() { + let mut m = TrieMap::new(); + assert!(m.insert(1, 12)); + assert!(m.insert(2, 8)); + assert!(m.insert(5, 14)); + let new = 100; + match m.find_mut(&5) { + None => fail!(), Some(x) => *x = new + } + assert_eq!(m.find(&5), Some(&new)); + } + + #[test] + fn test_step() { + let mut trie = TrieMap::new(); + let n = 300; + + for uint::range_step(1, n, 2) |x| { + assert!(trie.insert(x, x + 1)); + assert!(trie.contains_key(&x)); + check_integrity(&trie.root); + } + + for uint::range_step(0, n, 2) |x| { + assert!(!trie.contains_key(&x)); + assert!(trie.insert(x, x + 1)); + check_integrity(&trie.root); + } + + for uint::range(0, n) |x| { + assert!(trie.contains_key(&x)); + assert!(!trie.insert(x, x + 1)); + check_integrity(&trie.root); + } + + for uint::range_step(1, n, 2) |x| { + assert!(trie.remove(&x)); + assert!(!trie.contains_key(&x)); + check_integrity(&trie.root); + } + + for uint::range_step(0, n, 2) |x| { + assert!(trie.contains_key(&x)); + assert!(!trie.insert(x, x + 1)); + check_integrity(&trie.root); + } + } + + #[test] + fn test_each() { + let mut m = TrieMap::new(); + + assert!(m.insert(3, 6)); + assert!(m.insert(0, 0)); + assert!(m.insert(4, 8)); + assert!(m.insert(2, 4)); + assert!(m.insert(1, 2)); + + let mut n = 0; + for m.each |k, v| { + assert_eq!(*k, n); + assert_eq!(*v, n * 2); + n += 1; + } + } + + #[test] + fn test_each_break() { + let mut m = TrieMap::new(); + + for uint::range_rev(uint::max_value, uint::max_value - 10000) |x| { + m.insert(x, x / 2); + } + + let mut n = uint::max_value - 9999; + for m.each |k, v| { + if n == uint::max_value - 5000 { break } + assert!(n < uint::max_value - 5000); + + assert_eq!(*k, n); + assert_eq!(*v, n / 2); + n += 1; + } + } + + #[test] + fn test_each_reverse() { + let mut m = TrieMap::new(); + + assert!(m.insert(3, 6)); + assert!(m.insert(0, 0)); + assert!(m.insert(4, 8)); + assert!(m.insert(2, 4)); + assert!(m.insert(1, 2)); + + let mut n = 4; + for m.each_reverse |k, v| { + assert_eq!(*k, n); + assert_eq!(*v, n * 2); + n -= 1; + } + } + + #[test] + fn test_each_reverse_break() { + let mut m = TrieMap::new(); + + for uint::range_rev(uint::max_value, uint::max_value - 10000) |x| { + m.insert(x, x / 2); + } + + let mut n = uint::max_value; + for m.each_reverse |k, v| { + if n == uint::max_value - 5000 { break } + assert!(n > uint::max_value - 5000); + + assert_eq!(*k, n); + assert_eq!(*v, n / 2); + n -= 1; + } + } + + #[test] + fn test_sane_chunk() { + let x = 1; + let y = 1 << (uint::bits - 1); + + let mut trie = TrieSet::new(); + + assert!(trie.insert(x)); + assert!(trie.insert(y)); + + assert_eq!(trie.len(), 2); + + let expected = [x, y]; + + let mut i = 0; + + for trie.each |x| { + assert_eq!(expected[i], *x); + i += 1; + } + } + + #[test] + fn test_swap() { + let mut m = TrieMap::new(); + assert_eq!(m.swap(1, 2), None); + assert_eq!(m.swap(1, 3), Some(2)); + assert_eq!(m.swap(1, 4), Some(3)); + } + + #[test] + fn test_pop() { + let mut m = TrieMap::new(); + m.insert(1, 2); + assert_eq!(m.pop(&1), Some(2)); + assert_eq!(m.pop(&1), None); + } +} diff --git a/src/libstd/tuple.rs b/src/libstd/tuple.rs new file mode 100644 index 00000000000..639df89a377 --- /dev/null +++ b/src/libstd/tuple.rs @@ -0,0 +1,439 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Operations on tuples + +use kinds::Copy; +use vec; + +pub use self::inner::*; + +pub trait CopyableTuple<T, U> { + fn first(&self) -> T; + fn second(&self) -> U; + fn swap(&self) -> (U, T); +} + +impl<T:Copy,U:Copy> CopyableTuple<T, U> for (T, U) { + /// Return the first element of self + #[inline(always)] + fn first(&self) -> T { + match *self { + (t, _) => t, + } + } + + /// Return the second element of self + #[inline(always)] + fn second(&self) -> U { + match *self { + (_, u) => u, + } + } + + /// Return the results of swapping the two elements of self + #[inline(always)] + fn swap(&self) -> (U, T) { + match *self { + (t, u) => (u, t), + } + } +} + +pub trait ImmutableTuple<T, U> { + fn first_ref<'a>(&'a self) -> &'a T; + fn second_ref<'a>(&'a self) -> &'a U; +} + +impl<T, U> ImmutableTuple<T, U> for (T, U) { + #[inline(always)] + fn first_ref<'a>(&'a self) -> &'a T { + match *self { + (ref t, _) => t, + } + } + #[inline(always)] + fn second_ref<'a>(&'a self) -> &'a U { + match *self { + (_, ref u) => u, + } + } +} + +pub trait ExtendedTupleOps<A,B> { + fn zip(&self) -> ~[(A, B)]; + fn map<C>(&self, f: &fn(a: &A, b: &B) -> C) -> ~[C]; +} + +impl<'self,A:Copy,B:Copy> ExtendedTupleOps<A,B> for (&'self [A], &'self [B]) { + #[inline(always)] + fn zip(&self) -> ~[(A, B)] { + match *self { + (ref a, ref b) => { + vec::zip_slice(*a, *b) + } + } + } + + #[inline(always)] + fn map<C>(&self, f: &fn(a: &A, b: &B) -> C) -> ~[C] { + match *self { + (ref a, ref b) => { + vec::map_zip(*a, *b, f) + } + } + } +} + +impl<A:Copy,B:Copy> ExtendedTupleOps<A,B> for (~[A], ~[B]) { + #[inline(always)] + fn zip(&self) -> ~[(A, B)] { + match *self { + (ref a, ref b) => { + vec::zip_slice(*a, *b) + } + } + } + + #[inline(always)] + fn map<C>(&self, f: &fn(a: &A, b: &B) -> C) -> ~[C] { + match *self { + (ref a, ref b) => { + vec::map_zip(*a, *b, f) + } + } + } +} + +// macro for implementing n-ary tuple functions and operations + +macro_rules! tuple_impls { + ($( + ($cloneable_trait:ident, $immutable_trait:ident) { + $(($get_fn:ident, $get_ref_fn:ident) -> $T:ident { + $get_pattern:pat => $ret:expr + })+ + } + )+) => { + pub mod inner { + use clone::Clone; + #[cfg(not(test))] use cmp::*; + + $( + pub trait $cloneable_trait<$($T),+> { + $(fn $get_fn(&self) -> $T;)+ + } + + impl<$($T:Clone),+> $cloneable_trait<$($T),+> for ($($T),+) { + $( + #[inline(always)] + fn $get_fn(&self) -> $T { + self.$get_ref_fn().clone() + } + )+ + } + + pub trait $immutable_trait<$($T),+> { + $(fn $get_ref_fn<'a>(&'a self) -> &'a $T;)+ + } + + impl<$($T),+> $immutable_trait<$($T),+> for ($($T),+) { + $( + #[inline(always)] + fn $get_ref_fn<'a>(&'a self) -> &'a $T { + match *self { $get_pattern => $ret } + } + )+ + } + + impl<$($T:Clone),+> Clone for ($($T),+) { + fn clone(&self) -> ($($T),+) { + ($(self.$get_ref_fn().clone()),+) + } + } + + #[cfg(not(test))] + impl<$($T:Eq),+> Eq for ($($T),+) { + #[inline(always)] + fn eq(&self, other: &($($T),+)) -> bool { + $(*self.$get_ref_fn() == *other.$get_ref_fn())&&+ + } + #[inline(always)] + fn ne(&self, other: &($($T),+)) -> bool { + !(*self == *other) + } + } + + #[cfg(not(test))] + impl<$($T:TotalEq),+> TotalEq for ($($T),+) { + #[inline(always)] + fn equals(&self, other: &($($T),+)) -> bool { + $(self.$get_ref_fn().equals(other.$get_ref_fn()))&&+ + } + } + + #[cfg(not(test))] + impl<$($T:Ord),+> Ord for ($($T),+) { + #[inline(always)] + fn lt(&self, other: &($($T),+)) -> bool { + lexical_lt!($(self.$get_ref_fn(), other.$get_ref_fn()),+) + } + #[inline(always)] + fn le(&self, other: &($($T),+)) -> bool { !(*other).lt(&(*self)) } + #[inline(always)] + fn ge(&self, other: &($($T),+)) -> bool { !(*self).lt(other) } + #[inline(always)] + fn gt(&self, other: &($($T),+)) -> bool { (*other).lt(&(*self)) } + } + + #[cfg(not(test))] + impl<$($T:TotalOrd),+> TotalOrd for ($($T),+) { + #[inline] + fn cmp(&self, other: &($($T),+)) -> Ordering { + lexical_cmp!($(self.$get_ref_fn(), other.$get_ref_fn()),+) + } + } + )+ + } + } +} + +// Constructs an expression that performs a lexical less-than +// ordering. The values are interleaved, so the macro invocation for +// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_lt!(a1, b1, a2, b2, +// a3, b3)` (and similarly for `lexical_cmp`) +macro_rules! lexical_lt { + ($a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => { + if *$a < *$b { true } + else if !(*$b < *$a) { lexical_lt!($($rest_a, $rest_b),+) } + else { false } + }; + ($a:expr, $b:expr) => { *$a < *$b }; +} + +macro_rules! lexical_cmp { + ($a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => { + match ($a).cmp($b) { + Equal => lexical_cmp!($($rest_a, $rest_b),+), + ordering => ordering + } + }; + ($a:expr, $b:expr) => { ($a).cmp($b) }; +} + + +tuple_impls! { + (CloneableTuple2, ImmutableTuple2) { + (n0, n0_ref) -> A { (ref a,_) => a } + (n1, n1_ref) -> B { (_,ref b) => b } + } + + (CloneableTuple3, ImmutableTuple3) { + (n0, n0_ref) -> A { (ref a,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_) => b } + (n2, n2_ref) -> C { (_,_,ref c) => c } + } + + (CloneableTuple4, ImmutableTuple4) { + (n0, n0_ref) -> A { (ref a,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d) => d } + } + + (CloneableTuple5, ImmutableTuple5) { + (n0, n0_ref) -> A { (ref a,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e) => e } + } + + (CloneableTuple6, ImmutableTuple6) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f) => f } + } + + (CloneableTuple7, ImmutableTuple7) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g) => g } + } + + (CloneableTuple8, ImmutableTuple8) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_) => g } + (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h) => h } + } + + (CloneableTuple9, ImmutableTuple9) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_) => g } + (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_) => h } + (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i) => i } + } + + (CloneableTuple10, ImmutableTuple10) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_,_) => g } + (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_,_) => h } + (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i,_) => i } + (n9, n9_ref) -> J { (_,_,_,_,_,_,_,_,_,ref j) => j } + } + + (CloneableTuple11, ImmutableTuple11) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_,_,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_,_,_) => g } + (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_,_,_) => h } + (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i,_,_) => i } + (n9, n9_ref) -> J { (_,_,_,_,_,_,_,_,_,ref j,_) => j } + (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,ref k) => k } + } + + (CloneableTuple12, ImmutableTuple12) { + (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_,_,_,_) => a } + (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_,_,_,_) => b } + (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_,_,_,_) => c } + (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_,_,_,_) => d } + (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_,_,_,_) => e } + (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_,_,_,_) => f } + (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_,_,_,_) => g } + (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_,_,_,_) => h } + (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i,_,_,_) => i } + (n9, n9_ref) -> J { (_,_,_,_,_,_,_,_,_,ref j,_,_) => j } + (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,ref k,_) => k } + (n11, n11_ref) -> L { (_,_,_,_,_,_,_,_,_,_,_,ref l) => l } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use clone::Clone; + use cmp::*; + + #[test] + fn test_tuple_ref() { + let x = (~"foo", ~"bar"); + assert_eq!(x.first_ref(), &~"foo"); + assert_eq!(x.second_ref(), &~"bar"); + } + + #[test] + #[allow(non_implicitly_copyable_typarams)] + fn test_tuple() { + assert_eq!((948, 4039.48).first(), 948); + assert_eq!((34.5, ~"foo").second(), ~"foo"); + assert_eq!(('a', 2).swap(), (2, 'a')); + } + + #[test] + fn test_clone() { + let a = (1, ~"2"); + let b = a.clone(); + assert_eq!(a.first(), b.first()); + assert_eq!(a.second(), b.second()); + } + + #[test] + fn test_n_tuple() { + let t = (0u8, 1u16, 2u32, 3u64, 4u, 5i8, 6i16, 7i32, 8i64, 9i, 10f32, 11f64); + assert_eq!(t.n0(), 0u8); + assert_eq!(t.n1(), 1u16); + assert_eq!(t.n2(), 2u32); + assert_eq!(t.n3(), 3u64); + assert_eq!(t.n4(), 4u); + assert_eq!(t.n5(), 5i8); + assert_eq!(t.n6(), 6i16); + assert_eq!(t.n7(), 7i32); + assert_eq!(t.n8(), 8i64); + assert_eq!(t.n9(), 9i); + assert_eq!(t.n10(), 10f32); + assert_eq!(t.n11(), 11f64); + + assert_eq!(t.n0_ref(), &0u8); + assert_eq!(t.n1_ref(), &1u16); + assert_eq!(t.n2_ref(), &2u32); + assert_eq!(t.n3_ref(), &3u64); + assert_eq!(t.n4_ref(), &4u); + assert_eq!(t.n5_ref(), &5i8); + assert_eq!(t.n6_ref(), &6i16); + assert_eq!(t.n7_ref(), &7i32); + assert_eq!(t.n8_ref(), &8i64); + assert_eq!(t.n9_ref(), &9i); + assert_eq!(t.n10_ref(), &10f32); + assert_eq!(t.n11_ref(), &11f64); + } + + #[test] + fn test_tuple_cmp() { + let small = (1u, 2u, 3u), big = (3u, 2u, 1u); + + // Eq + assert_eq!(small, small); + assert_eq!(big, big); + assert!(small != big); + assert!(big != small); + + // Ord + assert!(small < big); + assert!(!(small < small)); + assert!(!(big < small)); + assert!(!(big < big)); + + assert!(small <= small); + assert!(big <= big); + + assert!(big > small); + assert!(small >= small); + assert!(big >= small); + assert!(big >= big); + + // TotalEq + assert!(small.equals(&small)); + assert!(big.equals(&big)); + assert!(!small.equals(&big)); + assert!(!big.equals(&small)); + + // TotalOrd + assert_eq!(small.cmp(&small), Equal); + assert_eq!(big.cmp(&big), Equal); + assert_eq!(small.cmp(&big), Less); + assert_eq!(big.cmp(&small), Greater); + } +} diff --git a/src/libstd/unicode.rs b/src/libstd/unicode.rs index 77996de6d83..d6e2c5eee6a 100644 --- a/src/libstd/unicode.rs +++ b/src/libstd/unicode.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,241 +8,2637 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[forbid(deprecated_mode)]; - -pub mod icu { - pub type UBool = u8; - pub type UProperty = int; - pub type UChar32 = char; - - pub static TRUE : u8 = 1u8; - pub static FALSE : u8 = 1u8; - - pub static UCHAR_ALPHABETIC : UProperty = 0; - pub static UCHAR_BINARY_START : UProperty = 0; // = UCHAR_ALPHABETIC - pub static UCHAR_ASCII_HEX_DIGIT : UProperty = 1; - pub static UCHAR_BIDI_CONTROL : UProperty = 2; - - pub static UCHAR_BIDI_MIRRORED : UProperty = 3; - pub static UCHAR_DASH : UProperty = 4; - pub static UCHAR_DEFAULT_IGNORABLE_CODE_POINT : UProperty = 5; - pub static UCHAR_DEPRECATED : UProperty = 6; - - pub static UCHAR_DIACRITIC : UProperty = 7; - pub static UCHAR_EXTENDER : UProperty = 8; - pub static UCHAR_FULL_COMPOSITION_EXCLUSION : UProperty = 9; - pub static UCHAR_GRAPHEME_BASE : UProperty = 10; - - pub static UCHAR_GRAPHEME_EXTEND : UProperty = 11; - pub static UCHAR_GRAPHEME_LINK : UProperty = 12; - pub static UCHAR_HEX_DIGIT : UProperty = 13; - pub static UCHAR_HYPHEN : UProperty = 14; - - pub static UCHAR_ID_CONTINUE : UProperty = 15; - pub static UCHAR_ID_START : UProperty = 16; - pub static UCHAR_IDEOGRAPHIC : UProperty = 17; - pub static UCHAR_IDS_BINARY_OPERATOR : UProperty = 18; - - pub static UCHAR_IDS_TRINARY_OPERATOR : UProperty = 19; - pub static UCHAR_JOIN_CONTROL : UProperty = 20; - pub static UCHAR_LOGICAL_ORDER_EXCEPTION : UProperty = 21; - pub static UCHAR_LOWERCASE : UProperty = 22; - - pub static UCHAR_MATH : UProperty = 23; - pub static UCHAR_NONCHARACTER_CODE_POINT : UProperty = 24; - pub static UCHAR_QUOTATION_MARK : UProperty = 25; - pub static UCHAR_RADICAL : UProperty = 26; - - pub static UCHAR_SOFT_DOTTED : UProperty = 27; - pub static UCHAR_TERMINAL_PUNCTUATION : UProperty = 28; - pub static UCHAR_UNIFIED_IDEOGRAPH : UProperty = 29; - pub static UCHAR_UPPERCASE : UProperty = 30; - - pub static UCHAR_WHITE_SPACE : UProperty = 31; - pub static UCHAR_XID_CONTINUE : UProperty = 32; - pub static UCHAR_XID_START : UProperty = 33; - pub static UCHAR_CASE_SENSITIVE : UProperty = 34; - - pub static UCHAR_S_TERM : UProperty = 35; - pub static UCHAR_VARIATION_SELECTOR : UProperty = 36; - pub static UCHAR_NFD_INERT : UProperty = 37; - pub static UCHAR_NFKD_INERT : UProperty = 38; - - pub static UCHAR_NFC_INERT : UProperty = 39; - pub static UCHAR_NFKC_INERT : UProperty = 40; - pub static UCHAR_SEGMENT_STARTER : UProperty = 41; - pub static UCHAR_PATTERN_SYNTAX : UProperty = 42; - - pub static UCHAR_PATTERN_WHITE_SPACE : UProperty = 43; - pub static UCHAR_POSIX_ALNUM : UProperty = 44; - pub static UCHAR_POSIX_BLANK : UProperty = 45; - pub static UCHAR_POSIX_GRAPH : UProperty = 46; - - pub static UCHAR_POSIX_PRINT : UProperty = 47; - pub static UCHAR_POSIX_XDIGIT : UProperty = 48; - pub static UCHAR_CASED : UProperty = 49; - pub static UCHAR_CASE_IGNORABLE : UProperty = 50; - - pub static UCHAR_CHANGES_WHEN_LOWERCASED : UProperty = 51; - pub static UCHAR_CHANGES_WHEN_UPPERCASED : UProperty = 52; - pub static UCHAR_CHANGES_WHEN_TITLECASED : UProperty = 53; - pub static UCHAR_CHANGES_WHEN_CASEFOLDED : UProperty = 54; - - pub static UCHAR_CHANGES_WHEN_CASEMAPPED : UProperty = 55; - pub static UCHAR_CHANGES_WHEN_NFKC_CASEFOLDED : UProperty = 56; - pub static UCHAR_BINARY_LIMIT : UProperty = 57; - pub static UCHAR_BIDI_CLASS : UProperty = 0x1000; - - pub static UCHAR_INT_START : UProperty = 0x1000; // UCHAR_BIDI_CLASS - pub static UCHAR_BLOCK : UProperty = 0x1001; - pub static UCHAR_CANONICAL_COMBINING_CLASS : UProperty = 0x1002; - pub static UCHAR_DECOMPOSITION_TYPE : UProperty = 0x1003; - - pub static UCHAR_EAST_ASIAN_WIDTH : UProperty = 0x1004; - pub static UCHAR_GENERAL_CATEGORY : UProperty = 0x1005; - pub static UCHAR_JOINING_GROUP : UProperty = 0x1006; - pub static UCHAR_JOINING_TYPE : UProperty = 0x1007; - - pub static UCHAR_LINE_BREAK : UProperty = 0x1008; - pub static UCHAR_NUMERIC_TYPE : UProperty = 0x1009; - pub static UCHAR_SCRIPT : UProperty = 0x100A; - pub static UCHAR_HANGUL_SYLLABLE_TYPE : UProperty = 0x100B; - - pub static UCHAR_NFD_QUICK_CHECK : UProperty = 0x100C; - pub static UCHAR_NFKD_QUICK_CHECK : UProperty = 0x100D; - pub static UCHAR_NFC_QUICK_CHECK : UProperty = 0x100E; - pub static UCHAR_NFKC_QUICK_CHECK : UProperty = 0x100F; - - pub static UCHAR_LEAD_CANONICAL_COMBINING_CLASS : UProperty = 0x1010; - pub static UCHAR_TRAIL_CANONICAL_COMBINING_CLASS : UProperty = 0x1011; - pub static UCHAR_GRAPHEME_CLUSTER_BREAK : UProperty = 0x1012; - pub static UCHAR_SENTENCE_BREAK : UProperty = 0x1013; - - pub static UCHAR_WORD_BREAK : UProperty = 0x1014; - pub static UCHAR_INT_LIMIT : UProperty = 0x1015; - - pub static UCHAR_GENERAL_CATEGORY_MASK : UProperty = 0x2000; - pub static UCHAR_MASK_START : UProperty = 0x2000; - // = UCHAR_GENERAL_CATEGORY_MASK - pub static UCHAR_MASK_LIMIT : UProperty = 0x2001; - - pub static UCHAR_NUMERIC_VALUE : UProperty = 0x3000; - pub static UCHAR_DOUBLE_START : UProperty = 0x3000; - // = UCHAR_NUMERIC_VALUE - pub static UCHAR_DOUBLE_LIMIT : UProperty = 0x3001; - - pub static UCHAR_AGE : UProperty = 0x4000; - pub static UCHAR_STRING_START : UProperty = 0x4000; // = UCHAR_AGE - pub static UCHAR_BIDI_MIRRORING_GLYPH : UProperty = 0x4001; - pub static UCHAR_CASE_FOLDING : UProperty = 0x4002; - - pub static UCHAR_ISO_COMMENT : UProperty = 0x4003; - pub static UCHAR_LOWERCASE_MAPPING : UProperty = 0x4004; - pub static UCHAR_NAME : UProperty = 0x4005; - pub static UCHAR_SIMPLE_CASE_FOLDING : UProperty = 0x4006; - - pub static UCHAR_SIMPLE_LOWERCASE_MAPPING : UProperty = 0x4007; - pub static UCHAR_SIMPLE_TITLECASE_MAPPING : UProperty = 0x4008; - pub static UCHAR_SIMPLE_UPPERCASE_MAPPING : UProperty = 0x4009; - pub static UCHAR_TITLECASE_MAPPING : UProperty = 0x400A; - - pub static UCHAR_UNICODE_1_NAME : UProperty = 0x400B; - pub static UCHAR_UPPERCASE_MAPPING : UProperty = 0x400C; - pub static UCHAR_STRING_LIMIT : UProperty = 0x400D; - - pub static UCHAR_SCRIPT_EXTENSIONS : UProperty = 0x7000; - pub static UCHAR_OTHER_PROPERTY_START : UProperty = 0x7000; - // = UCHAR_SCRIPT_EXTENSIONS; - pub static UCHAR_OTHER_PROPERTY_LIMIT : UProperty = 0x7001; - - pub static UCHAR_INVALID_CODE : UProperty = -1; - - pub mod libicu { - #[link_name = "icuuc"] - #[abi = "cdecl"] - pub extern { - unsafe fn u_hasBinaryProperty(c: UChar32, which: UProperty) - -> UBool; - unsafe fn u_isdigit(c: UChar32) -> UBool; - unsafe fn u_islower(c: UChar32) -> UBool; - unsafe fn u_isspace(c: UChar32) -> UBool; - unsafe fn u_isupper(c: UChar32) -> UBool; - unsafe fn u_tolower(c: UChar32) -> UChar32; - unsafe fn u_toupper(c: UChar32) -> UChar32; - } +#[doc(hidden)]; // FIXME #3538 + +// The following code was generated by "src/etc/unicode.py" + +pub mod general_category { + + fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { + use cmp::{Equal, Less, Greater}; + use vec::bsearch; + use option::None; + (do bsearch(r) |&(lo,hi)| { + if lo <= c && c <= hi { Equal } + else if hi < c { Less } + else { Greater } + }) != None } -} -pub fn is_XID_start(c: char) -> bool { - return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) - == icu::TRUE; -} -pub fn is_XID_continue(c: char) -> bool { - return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) - == icu::TRUE; -} + static Cc_table : &'static [(char,char)] = &[ + ('\x00', '\x1f'), ('\x7f', '\x9f') + ]; -/* -Function: is_digit + pub fn Cc(c: char) -> bool { + bsearch_range_table(c, Cc_table) + } -Returns true if a character is a digit. -*/ -pub fn is_digit(c: char) -> bool { - return icu::libicu::u_isdigit(c) == icu::TRUE; -} + static Cf_table : &'static [(char,char)] = &[ + ('\xad', '\xad'), ('\u0600', '\u0604'), + ('\u06dd', '\u06dd'), ('\u070f', '\u070f'), + ('\u200b', '\u200f'), ('\u202a', '\u202e'), + ('\u2060', '\u206f'), ('\ufeff', '\ufeff'), + ('\ufff9', '\ufffb'), ('\U000110bd', '\U000110bd'), + ('\U0001d173', '\U0001d17a'), ('\U000e0001', '\U000e007f') + ]; -/* -Function: is_lower + pub fn Cf(c: char) -> bool { + bsearch_range_table(c, Cf_table) + } -Returns true if a character is a lowercase letter. -*/ -pub fn is_lower(c: char) -> bool { - return icu::libicu::u_islower(c) == icu::TRUE; -} + static Co_table : &'static [(char,char)] = &[ + ('\ue000', '\uf8ff') + ]; -/* -Function: is_space + pub fn Co(c: char) -> bool { + bsearch_range_table(c, Co_table) + } -Returns true if a character is space. -*/ -pub fn is_space(c: char) -> bool { - return icu::libicu::u_isspace(c) == icu::TRUE; -} + static Cs_table : &'static [(char,char)] = &[ + ('\ud800', '\udfff') + ]; + + pub fn Cs(c: char) -> bool { + bsearch_range_table(c, Cs_table) + } + + static Ll_table : &'static [(char,char)] = &[ + ('\x61', '\x7a'), ('\xb5', '\xb5'), + ('\xdf', '\xf6'), ('\xf8', '\xff'), + ('\u0101', '\u0101'), ('\u0103', '\u0103'), + ('\u0105', '\u0105'), ('\u0107', '\u0107'), + ('\u0109', '\u0109'), ('\u010b', '\u010b'), + ('\u010d', '\u010d'), ('\u010f', '\u010f'), + ('\u0111', '\u0111'), ('\u0113', '\u0113'), + ('\u0115', '\u0115'), ('\u0117', '\u0117'), + ('\u0119', '\u0119'), ('\u011b', '\u011b'), + ('\u011d', '\u011d'), ('\u011f', '\u011f'), + ('\u0121', '\u0121'), ('\u0123', '\u0123'), + ('\u0125', '\u0125'), ('\u0127', '\u0127'), + ('\u0129', '\u0129'), ('\u012b', '\u012b'), + ('\u012d', '\u012d'), ('\u012f', '\u012f'), + ('\u0131', '\u0131'), ('\u0133', '\u0133'), + ('\u0135', '\u0135'), ('\u0137', '\u0138'), + ('\u013a', '\u013a'), ('\u013c', '\u013c'), + ('\u013e', '\u013e'), ('\u0140', '\u0140'), + ('\u0142', '\u0142'), ('\u0144', '\u0144'), + ('\u0146', '\u0146'), ('\u0148', '\u0149'), + ('\u014b', '\u014b'), ('\u014d', '\u014d'), + ('\u014f', '\u014f'), ('\u0151', '\u0151'), + ('\u0153', '\u0153'), ('\u0155', '\u0155'), + ('\u0157', '\u0157'), ('\u0159', '\u0159'), + ('\u015b', '\u015b'), ('\u015d', '\u015d'), + ('\u015f', '\u015f'), ('\u0161', '\u0161'), + ('\u0163', '\u0163'), ('\u0165', '\u0165'), + ('\u0167', '\u0167'), ('\u0169', '\u0169'), + ('\u016b', '\u016b'), ('\u016d', '\u016d'), + ('\u016f', '\u016f'), ('\u0171', '\u0171'), + ('\u0173', '\u0173'), ('\u0175', '\u0175'), + ('\u0177', '\u0177'), ('\u017a', '\u017a'), + ('\u017c', '\u017c'), ('\u017e', '\u0180'), + ('\u0183', '\u0183'), ('\u0185', '\u0185'), + ('\u0188', '\u0188'), ('\u018c', '\u018d'), + ('\u0192', '\u0192'), ('\u0195', '\u0195'), + ('\u0199', '\u019b'), ('\u019e', '\u019e'), + ('\u01a1', '\u01a1'), ('\u01a3', '\u01a3'), + ('\u01a5', '\u01a5'), ('\u01a8', '\u01a8'), + ('\u01aa', '\u01ab'), ('\u01ad', '\u01ad'), + ('\u01b0', '\u01b0'), ('\u01b4', '\u01b4'), + ('\u01b6', '\u01b6'), ('\u01b9', '\u01ba'), + ('\u01bd', '\u01bf'), ('\u01c6', '\u01c6'), + ('\u01c9', '\u01c9'), ('\u01cc', '\u01cc'), + ('\u01ce', '\u01ce'), ('\u01d0', '\u01d0'), + ('\u01d2', '\u01d2'), ('\u01d4', '\u01d4'), + ('\u01d6', '\u01d6'), ('\u01d8', '\u01d8'), + ('\u01da', '\u01da'), ('\u01dc', '\u01dd'), + ('\u01df', '\u01df'), ('\u01e1', '\u01e1'), + ('\u01e3', '\u01e3'), ('\u01e5', '\u01e5'), + ('\u01e7', '\u01e7'), ('\u01e9', '\u01e9'), + ('\u01eb', '\u01eb'), ('\u01ed', '\u01ed'), + ('\u01ef', '\u01f0'), ('\u01f3', '\u01f3'), + ('\u01f5', '\u01f5'), ('\u01f9', '\u01f9'), + ('\u01fb', '\u01fb'), ('\u01fd', '\u01fd'), + ('\u01ff', '\u01ff'), ('\u0201', '\u0201'), + ('\u0203', '\u0203'), ('\u0205', '\u0205'), + ('\u0207', '\u0207'), ('\u0209', '\u0209'), + ('\u020b', '\u020b'), ('\u020d', '\u020d'), + ('\u020f', '\u020f'), ('\u0211', '\u0211'), + ('\u0213', '\u0213'), ('\u0215', '\u0215'), + ('\u0217', '\u0217'), ('\u0219', '\u0219'), + ('\u021b', '\u021b'), ('\u021d', '\u021d'), + ('\u021f', '\u021f'), ('\u0221', '\u0221'), + ('\u0223', '\u0223'), ('\u0225', '\u0225'), + ('\u0227', '\u0227'), ('\u0229', '\u0229'), + ('\u022b', '\u022b'), ('\u022d', '\u022d'), + ('\u022f', '\u022f'), ('\u0231', '\u0231'), + ('\u0233', '\u0239'), ('\u023c', '\u023c'), + ('\u023f', '\u0240'), ('\u0242', '\u0242'), + ('\u0247', '\u0247'), ('\u0249', '\u0249'), + ('\u024b', '\u024b'), ('\u024d', '\u024d'), + ('\u024f', '\u0293'), ('\u0295', '\u02af'), + ('\u0371', '\u0371'), ('\u0373', '\u0373'), + ('\u0377', '\u0377'), ('\u037b', '\u037d'), + ('\u0390', '\u0390'), ('\u03ac', '\u03ce'), + ('\u03d0', '\u03d1'), ('\u03d5', '\u03d7'), + ('\u03d9', '\u03d9'), ('\u03db', '\u03db'), + ('\u03dd', '\u03dd'), ('\u03df', '\u03df'), + ('\u03e1', '\u03e1'), ('\u03e3', '\u03e3'), + ('\u03e5', '\u03e5'), ('\u03e7', '\u03e7'), + ('\u03e9', '\u03e9'), ('\u03eb', '\u03eb'), + ('\u03ed', '\u03ed'), ('\u03ef', '\u03f3'), + ('\u03f5', '\u03f5'), ('\u03f8', '\u03f8'), + ('\u03fb', '\u03fc'), ('\u0430', '\u045f'), + ('\u0461', '\u0461'), ('\u0463', '\u0463'), + ('\u0465', '\u0465'), ('\u0467', '\u0467'), + ('\u0469', '\u0469'), ('\u046b', '\u046b'), + ('\u046d', '\u046d'), ('\u046f', '\u046f'), + ('\u0471', '\u0471'), ('\u0473', '\u0473'), + ('\u0475', '\u0475'), ('\u0477', '\u0477'), + ('\u0479', '\u0479'), ('\u047b', '\u047b'), + ('\u047d', '\u047d'), ('\u047f', '\u047f'), + ('\u0481', '\u0481'), ('\u048b', '\u048b'), + ('\u048d', '\u048d'), ('\u048f', '\u048f'), + ('\u0491', '\u0491'), ('\u0493', '\u0493'), + ('\u0495', '\u0495'), ('\u0497', '\u0497'), + ('\u0499', '\u0499'), ('\u049b', '\u049b'), + ('\u049d', '\u049d'), ('\u049f', '\u049f'), + ('\u04a1', '\u04a1'), ('\u04a3', '\u04a3'), + ('\u04a5', '\u04a5'), ('\u04a7', '\u04a7'), + ('\u04a9', '\u04a9'), ('\u04ab', '\u04ab'), + ('\u04ad', '\u04ad'), ('\u04af', '\u04af'), + ('\u04b1', '\u04b1'), ('\u04b3', '\u04b3'), + ('\u04b5', '\u04b5'), ('\u04b7', '\u04b7'), + ('\u04b9', '\u04b9'), ('\u04bb', '\u04bb'), + ('\u04bd', '\u04bd'), ('\u04bf', '\u04bf'), + ('\u04c2', '\u04c2'), ('\u04c4', '\u04c4'), + ('\u04c6', '\u04c6'), ('\u04c8', '\u04c8'), + ('\u04ca', '\u04ca'), ('\u04cc', '\u04cc'), + ('\u04ce', '\u04cf'), ('\u04d1', '\u04d1'), + ('\u04d3', '\u04d3'), ('\u04d5', '\u04d5'), + ('\u04d7', '\u04d7'), ('\u04d9', '\u04d9'), + ('\u04db', '\u04db'), ('\u04dd', '\u04dd'), + ('\u04df', '\u04df'), ('\u04e1', '\u04e1'), + ('\u04e3', '\u04e3'), ('\u04e5', '\u04e5'), + ('\u04e7', '\u04e7'), ('\u04e9', '\u04e9'), + ('\u04eb', '\u04eb'), ('\u04ed', '\u04ed'), + ('\u04ef', '\u04ef'), ('\u04f1', '\u04f1'), + ('\u04f3', '\u04f3'), ('\u04f5', '\u04f5'), + ('\u04f7', '\u04f7'), ('\u04f9', '\u04f9'), + ('\u04fb', '\u04fb'), ('\u04fd', '\u04fd'), + ('\u04ff', '\u04ff'), ('\u0501', '\u0501'), + ('\u0503', '\u0503'), ('\u0505', '\u0505'), + ('\u0507', '\u0507'), ('\u0509', '\u0509'), + ('\u050b', '\u050b'), ('\u050d', '\u050d'), + ('\u050f', '\u050f'), ('\u0511', '\u0511'), + ('\u0513', '\u0513'), ('\u0515', '\u0515'), + ('\u0517', '\u0517'), ('\u0519', '\u0519'), + ('\u051b', '\u051b'), ('\u051d', '\u051d'), + ('\u051f', '\u051f'), ('\u0521', '\u0521'), + ('\u0523', '\u0523'), ('\u0525', '\u0525'), + ('\u0527', '\u0527'), ('\u0561', '\u0587'), + ('\u1d00', '\u1d2b'), ('\u1d6b', '\u1d77'), + ('\u1d79', '\u1d9a'), ('\u1e01', '\u1e01'), + ('\u1e03', '\u1e03'), ('\u1e05', '\u1e05'), + ('\u1e07', '\u1e07'), ('\u1e09', '\u1e09'), + ('\u1e0b', '\u1e0b'), ('\u1e0d', '\u1e0d'), + ('\u1e0f', '\u1e0f'), ('\u1e11', '\u1e11'), + ('\u1e13', '\u1e13'), ('\u1e15', '\u1e15'), + ('\u1e17', '\u1e17'), ('\u1e19', '\u1e19'), + ('\u1e1b', '\u1e1b'), ('\u1e1d', '\u1e1d'), + ('\u1e1f', '\u1e1f'), ('\u1e21', '\u1e21'), + ('\u1e23', '\u1e23'), ('\u1e25', '\u1e25'), + ('\u1e27', '\u1e27'), ('\u1e29', '\u1e29'), + ('\u1e2b', '\u1e2b'), ('\u1e2d', '\u1e2d'), + ('\u1e2f', '\u1e2f'), ('\u1e31', '\u1e31'), + ('\u1e33', '\u1e33'), ('\u1e35', '\u1e35'), + ('\u1e37', '\u1e37'), ('\u1e39', '\u1e39'), + ('\u1e3b', '\u1e3b'), ('\u1e3d', '\u1e3d'), + ('\u1e3f', '\u1e3f'), ('\u1e41', '\u1e41'), + ('\u1e43', '\u1e43'), ('\u1e45', '\u1e45'), + ('\u1e47', '\u1e47'), ('\u1e49', '\u1e49'), + ('\u1e4b', '\u1e4b'), ('\u1e4d', '\u1e4d'), + ('\u1e4f', '\u1e4f'), ('\u1e51', '\u1e51'), + ('\u1e53', '\u1e53'), ('\u1e55', '\u1e55'), + ('\u1e57', '\u1e57'), ('\u1e59', '\u1e59'), + ('\u1e5b', '\u1e5b'), ('\u1e5d', '\u1e5d'), + ('\u1e5f', '\u1e5f'), ('\u1e61', '\u1e61'), + ('\u1e63', '\u1e63'), ('\u1e65', '\u1e65'), + ('\u1e67', '\u1e67'), ('\u1e69', '\u1e69'), + ('\u1e6b', '\u1e6b'), ('\u1e6d', '\u1e6d'), + ('\u1e6f', '\u1e6f'), ('\u1e71', '\u1e71'), + ('\u1e73', '\u1e73'), ('\u1e75', '\u1e75'), + ('\u1e77', '\u1e77'), ('\u1e79', '\u1e79'), + ('\u1e7b', '\u1e7b'), ('\u1e7d', '\u1e7d'), + ('\u1e7f', '\u1e7f'), ('\u1e81', '\u1e81'), + ('\u1e83', '\u1e83'), ('\u1e85', '\u1e85'), + ('\u1e87', '\u1e87'), ('\u1e89', '\u1e89'), + ('\u1e8b', '\u1e8b'), ('\u1e8d', '\u1e8d'), + ('\u1e8f', '\u1e8f'), ('\u1e91', '\u1e91'), + ('\u1e93', '\u1e93'), ('\u1e95', '\u1e9d'), + ('\u1e9f', '\u1e9f'), ('\u1ea1', '\u1ea1'), + ('\u1ea3', '\u1ea3'), ('\u1ea5', '\u1ea5'), + ('\u1ea7', '\u1ea7'), ('\u1ea9', '\u1ea9'), + ('\u1eab', '\u1eab'), ('\u1ead', '\u1ead'), + ('\u1eaf', '\u1eaf'), ('\u1eb1', '\u1eb1'), + ('\u1eb3', '\u1eb3'), ('\u1eb5', '\u1eb5'), + ('\u1eb7', '\u1eb7'), ('\u1eb9', '\u1eb9'), + ('\u1ebb', '\u1ebb'), ('\u1ebd', '\u1ebd'), + ('\u1ebf', '\u1ebf'), ('\u1ec1', '\u1ec1'), + ('\u1ec3', '\u1ec3'), ('\u1ec5', '\u1ec5'), + ('\u1ec7', '\u1ec7'), ('\u1ec9', '\u1ec9'), + ('\u1ecb', '\u1ecb'), ('\u1ecd', '\u1ecd'), + ('\u1ecf', '\u1ecf'), ('\u1ed1', '\u1ed1'), + ('\u1ed3', '\u1ed3'), ('\u1ed5', '\u1ed5'), + ('\u1ed7', '\u1ed7'), ('\u1ed9', '\u1ed9'), + ('\u1edb', '\u1edb'), ('\u1edd', '\u1edd'), + ('\u1edf', '\u1edf'), ('\u1ee1', '\u1ee1'), + ('\u1ee3', '\u1ee3'), ('\u1ee5', '\u1ee5'), + ('\u1ee7', '\u1ee7'), ('\u1ee9', '\u1ee9'), + ('\u1eeb', '\u1eeb'), ('\u1eed', '\u1eed'), + ('\u1eef', '\u1eef'), ('\u1ef1', '\u1ef1'), + ('\u1ef3', '\u1ef3'), ('\u1ef5', '\u1ef5'), + ('\u1ef7', '\u1ef7'), ('\u1ef9', '\u1ef9'), + ('\u1efb', '\u1efb'), ('\u1efd', '\u1efd'), + ('\u1eff', '\u1f07'), ('\u1f10', '\u1f15'), + ('\u1f20', '\u1f27'), ('\u1f30', '\u1f37'), + ('\u1f40', '\u1f45'), ('\u1f50', '\u1f57'), + ('\u1f60', '\u1f67'), ('\u1f70', '\u1f87'), + ('\u1f90', '\u1f97'), ('\u1fa0', '\u1fa7'), + ('\u1fb0', '\u1fb7'), ('\u1fbe', '\u1fbe'), + ('\u1fc2', '\u1fc7'), ('\u1fd0', '\u1fd7'), + ('\u1fe0', '\u1fe7'), ('\u1ff2', '\u1ff7'), + ('\u210a', '\u210a'), ('\u210e', '\u210f'), + ('\u2113', '\u2113'), ('\u212f', '\u212f'), + ('\u2134', '\u2134'), ('\u2139', '\u2139'), + ('\u213c', '\u213d'), ('\u2146', '\u2149'), + ('\u214e', '\u214e'), ('\u2184', '\u2184'), + ('\u2c30', '\u2c5e'), ('\u2c61', '\u2c61'), + ('\u2c65', '\u2c66'), ('\u2c68', '\u2c68'), + ('\u2c6a', '\u2c6a'), ('\u2c6c', '\u2c6c'), + ('\u2c71', '\u2c71'), ('\u2c73', '\u2c74'), + ('\u2c76', '\u2c7b'), ('\u2c81', '\u2c81'), + ('\u2c83', '\u2c83'), ('\u2c85', '\u2c85'), + ('\u2c87', '\u2c87'), ('\u2c89', '\u2c89'), + ('\u2c8b', '\u2c8b'), ('\u2c8d', '\u2c8d'), + ('\u2c8f', '\u2c8f'), ('\u2c91', '\u2c91'), + ('\u2c93', '\u2c93'), ('\u2c95', '\u2c95'), + ('\u2c97', '\u2c97'), ('\u2c99', '\u2c99'), + ('\u2c9b', '\u2c9b'), ('\u2c9d', '\u2c9d'), + ('\u2c9f', '\u2c9f'), ('\u2ca1', '\u2ca1'), + ('\u2ca3', '\u2ca3'), ('\u2ca5', '\u2ca5'), + ('\u2ca7', '\u2ca7'), ('\u2ca9', '\u2ca9'), + ('\u2cab', '\u2cab'), ('\u2cad', '\u2cad'), + ('\u2caf', '\u2caf'), ('\u2cb1', '\u2cb1'), + ('\u2cb3', '\u2cb3'), ('\u2cb5', '\u2cb5'), + ('\u2cb7', '\u2cb7'), ('\u2cb9', '\u2cb9'), + ('\u2cbb', '\u2cbb'), ('\u2cbd', '\u2cbd'), + ('\u2cbf', '\u2cbf'), ('\u2cc1', '\u2cc1'), + ('\u2cc3', '\u2cc3'), ('\u2cc5', '\u2cc5'), + ('\u2cc7', '\u2cc7'), ('\u2cc9', '\u2cc9'), + ('\u2ccb', '\u2ccb'), ('\u2ccd', '\u2ccd'), + ('\u2ccf', '\u2ccf'), ('\u2cd1', '\u2cd1'), + ('\u2cd3', '\u2cd3'), ('\u2cd5', '\u2cd5'), + ('\u2cd7', '\u2cd7'), ('\u2cd9', '\u2cd9'), + ('\u2cdb', '\u2cdb'), ('\u2cdd', '\u2cdd'), + ('\u2cdf', '\u2cdf'), ('\u2ce1', '\u2ce1'), + ('\u2ce3', '\u2ce4'), ('\u2cec', '\u2cec'), + ('\u2cee', '\u2cee'), ('\u2cf3', '\u2cf3'), + ('\u2d00', '\u2d2d'), ('\ua641', '\ua641'), + ('\ua643', '\ua643'), ('\ua645', '\ua645'), + ('\ua647', '\ua647'), ('\ua649', '\ua649'), + ('\ua64b', '\ua64b'), ('\ua64d', '\ua64d'), + ('\ua64f', '\ua64f'), ('\ua651', '\ua651'), + ('\ua653', '\ua653'), ('\ua655', '\ua655'), + ('\ua657', '\ua657'), ('\ua659', '\ua659'), + ('\ua65b', '\ua65b'), ('\ua65d', '\ua65d'), + ('\ua65f', '\ua65f'), ('\ua661', '\ua661'), + ('\ua663', '\ua663'), ('\ua665', '\ua665'), + ('\ua667', '\ua667'), ('\ua669', '\ua669'), + ('\ua66b', '\ua66b'), ('\ua66d', '\ua66d'), + ('\ua681', '\ua681'), ('\ua683', '\ua683'), + ('\ua685', '\ua685'), ('\ua687', '\ua687'), + ('\ua689', '\ua689'), ('\ua68b', '\ua68b'), + ('\ua68d', '\ua68d'), ('\ua68f', '\ua68f'), + ('\ua691', '\ua691'), ('\ua693', '\ua693'), + ('\ua695', '\ua695'), ('\ua697', '\ua697'), + ('\ua723', '\ua723'), ('\ua725', '\ua725'), + ('\ua727', '\ua727'), ('\ua729', '\ua729'), + ('\ua72b', '\ua72b'), ('\ua72d', '\ua72d'), + ('\ua72f', '\ua731'), ('\ua733', '\ua733'), + ('\ua735', '\ua735'), ('\ua737', '\ua737'), + ('\ua739', '\ua739'), ('\ua73b', '\ua73b'), + ('\ua73d', '\ua73d'), ('\ua73f', '\ua73f'), + ('\ua741', '\ua741'), ('\ua743', '\ua743'), + ('\ua745', '\ua745'), ('\ua747', '\ua747'), + ('\ua749', '\ua749'), ('\ua74b', '\ua74b'), + ('\ua74d', '\ua74d'), ('\ua74f', '\ua74f'), + ('\ua751', '\ua751'), ('\ua753', '\ua753'), + ('\ua755', '\ua755'), ('\ua757', '\ua757'), + ('\ua759', '\ua759'), ('\ua75b', '\ua75b'), + ('\ua75d', '\ua75d'), ('\ua75f', '\ua75f'), + ('\ua761', '\ua761'), ('\ua763', '\ua763'), + ('\ua765', '\ua765'), ('\ua767', '\ua767'), + ('\ua769', '\ua769'), ('\ua76b', '\ua76b'), + ('\ua76d', '\ua76d'), ('\ua76f', '\ua76f'), + ('\ua771', '\ua778'), ('\ua77a', '\ua77a'), + ('\ua77c', '\ua77c'), ('\ua77f', '\ua77f'), + ('\ua781', '\ua781'), ('\ua783', '\ua783'), + ('\ua785', '\ua785'), ('\ua787', '\ua787'), + ('\ua78c', '\ua78c'), ('\ua78e', '\ua78e'), + ('\ua791', '\ua791'), ('\ua793', '\ua793'), + ('\ua7a1', '\ua7a1'), ('\ua7a3', '\ua7a3'), + ('\ua7a5', '\ua7a5'), ('\ua7a7', '\ua7a7'), + ('\ua7a9', '\ua7a9'), ('\ua7fa', '\ua7fa'), + ('\ufb00', '\ufb17'), ('\uff41', '\uff5a'), + ('\U00010428', '\U0001044f'), ('\U0001d41a', '\U0001d433'), + ('\U0001d44e', '\U0001d467'), ('\U0001d482', '\U0001d49b'), + ('\U0001d4b6', '\U0001d4cf'), ('\U0001d4ea', '\U0001d503'), + ('\U0001d51e', '\U0001d537'), ('\U0001d552', '\U0001d56b'), + ('\U0001d586', '\U0001d59f'), ('\U0001d5ba', '\U0001d5d3'), + ('\U0001d5ee', '\U0001d607'), ('\U0001d622', '\U0001d63b'), + ('\U0001d656', '\U0001d66f'), ('\U0001d68a', '\U0001d6a5'), + ('\U0001d6c2', '\U0001d6da'), ('\U0001d6dc', '\U0001d6e1'), + ('\U0001d6fc', '\U0001d714'), ('\U0001d716', '\U0001d71b'), + ('\U0001d736', '\U0001d74e'), ('\U0001d750', '\U0001d755'), + ('\U0001d770', '\U0001d788'), ('\U0001d78a', '\U0001d78f'), + ('\U0001d7aa', '\U0001d7c2'), ('\U0001d7c4', '\U0001d7c9'), + ('\U0001d7cb', '\U0001d7cb') + ]; + + pub fn Ll(c: char) -> bool { + bsearch_range_table(c, Ll_table) + } + + static Lm_table : &'static [(char,char)] = &[ + ('\u02b0', '\u02c1'), ('\u02c6', '\u02d1'), + ('\u02e0', '\u02e4'), ('\u02ec', '\u02ec'), + ('\u02ee', '\u02ee'), ('\u0374', '\u0374'), + ('\u037a', '\u037a'), ('\u0559', '\u0559'), + ('\u0640', '\u0640'), ('\u06e5', '\u06e6'), + ('\u07f4', '\u07f5'), ('\u07fa', '\u07fa'), + ('\u081a', '\u081a'), ('\u0824', '\u0824'), + ('\u0828', '\u0828'), ('\u0971', '\u0971'), + ('\u0e46', '\u0e46'), ('\u0ec6', '\u0ec6'), + ('\u10fc', '\u10fc'), ('\u17d7', '\u17d7'), + ('\u1843', '\u1843'), ('\u1aa7', '\u1aa7'), + ('\u1c78', '\u1c7d'), ('\u1d2c', '\u1d6a'), + ('\u1d78', '\u1d78'), ('\u1d9b', '\u1dbf'), + ('\u2071', '\u2071'), ('\u207f', '\u207f'), + ('\u2090', '\u209c'), ('\u2c7c', '\u2c7d'), + ('\u2d6f', '\u2d6f'), ('\u2e2f', '\u2e2f'), + ('\u3005', '\u3005'), ('\u3031', '\u3035'), + ('\u303b', '\u303b'), ('\u309d', '\u309e'), + ('\u30fc', '\u30fe'), ('\ua015', '\ua015'), + ('\ua4f8', '\ua4fd'), ('\ua60c', '\ua60c'), + ('\ua67f', '\ua67f'), ('\ua717', '\ua71f'), + ('\ua770', '\ua770'), ('\ua788', '\ua788'), + ('\ua7f8', '\ua7f9'), ('\ua9cf', '\ua9cf'), + ('\uaa70', '\uaa70'), ('\uaadd', '\uaadd'), + ('\uaaf3', '\uaaf4'), ('\uff70', '\uff70'), + ('\uff9e', '\uff9f'), ('\U00016f93', '\U00016f9f') + ]; + + pub fn Lm(c: char) -> bool { + bsearch_range_table(c, Lm_table) + } + + static Lo_table : &'static [(char,char)] = &[ + ('\xaa', '\xaa'), ('\xba', '\xba'), + ('\u01bb', '\u01bb'), ('\u01c0', '\u01c3'), + ('\u0294', '\u0294'), ('\u05d0', '\u05f2'), + ('\u0620', '\u063f'), ('\u0641', '\u064a'), + ('\u066e', '\u066f'), ('\u0671', '\u06d3'), + ('\u06d5', '\u06d5'), ('\u06ee', '\u06ef'), + ('\u06fa', '\u06fc'), ('\u06ff', '\u06ff'), + ('\u0710', '\u0710'), ('\u0712', '\u072f'), + ('\u074d', '\u07a5'), ('\u07b1', '\u07b1'), + ('\u07ca', '\u07ea'), ('\u0800', '\u0815'), + ('\u0840', '\u0858'), ('\u08a0', '\u08ac'), + ('\u0904', '\u0939'), ('\u093d', '\u093d'), + ('\u0950', '\u0950'), ('\u0958', '\u0961'), + ('\u0972', '\u097f'), ('\u0985', '\u09b9'), + ('\u09bd', '\u09bd'), ('\u09ce', '\u09ce'), + ('\u09dc', '\u09e1'), ('\u09f0', '\u09f1'), + ('\u0a05', '\u0a39'), ('\u0a59', '\u0a5e'), + ('\u0a72', '\u0a74'), ('\u0a85', '\u0ab9'), + ('\u0abd', '\u0abd'), ('\u0ad0', '\u0ae1'), + ('\u0b05', '\u0b39'), ('\u0b3d', '\u0b3d'), + ('\u0b5c', '\u0b61'), ('\u0b71', '\u0b71'), + ('\u0b83', '\u0bb9'), ('\u0bd0', '\u0bd0'), + ('\u0c05', '\u0c3d'), ('\u0c58', '\u0c61'), + ('\u0c85', '\u0cb9'), ('\u0cbd', '\u0cbd'), + ('\u0cde', '\u0ce1'), ('\u0cf1', '\u0cf2'), + ('\u0d05', '\u0d3d'), ('\u0d4e', '\u0d4e'), + ('\u0d60', '\u0d61'), ('\u0d7a', '\u0d7f'), + ('\u0d85', '\u0dc6'), ('\u0e01', '\u0e30'), + ('\u0e32', '\u0e33'), ('\u0e40', '\u0e45'), + ('\u0e81', '\u0eb0'), ('\u0eb2', '\u0eb3'), + ('\u0ebd', '\u0ec4'), ('\u0edc', '\u0f00'), + ('\u0f40', '\u0f6c'), ('\u0f88', '\u0f8c'), + ('\u1000', '\u102a'), ('\u103f', '\u103f'), + ('\u1050', '\u1055'), ('\u105a', '\u105d'), + ('\u1061', '\u1061'), ('\u1065', '\u1066'), + ('\u106e', '\u1070'), ('\u1075', '\u1081'), + ('\u108e', '\u108e'), ('\u10d0', '\u10fa'), + ('\u10fd', '\u135a'), ('\u1380', '\u138f'), + ('\u13a0', '\u13f4'), ('\u1401', '\u166c'), + ('\u166f', '\u167f'), ('\u1681', '\u169a'), + ('\u16a0', '\u16ea'), ('\u1700', '\u1711'), + ('\u1720', '\u1731'), ('\u1740', '\u1751'), + ('\u1760', '\u1770'), ('\u1780', '\u17b3'), + ('\u17dc', '\u17dc'), ('\u1820', '\u1842'), + ('\u1844', '\u18a8'), ('\u18aa', '\u191c'), + ('\u1950', '\u19ab'), ('\u19c1', '\u19c7'), + ('\u1a00', '\u1a16'), ('\u1a20', '\u1a54'), + ('\u1b05', '\u1b33'), ('\u1b45', '\u1b4b'), + ('\u1b83', '\u1ba0'), ('\u1bae', '\u1baf'), + ('\u1bba', '\u1be5'), ('\u1c00', '\u1c23'), + ('\u1c4d', '\u1c4f'), ('\u1c5a', '\u1c77'), + ('\u1ce9', '\u1cec'), ('\u1cee', '\u1cf1'), + ('\u1cf5', '\u1cf6'), ('\u2135', '\u2138'), + ('\u2d30', '\u2d67'), ('\u2d80', '\u2dde'), + ('\u3006', '\u3006'), ('\u303c', '\u303c'), + ('\u3041', '\u3096'), ('\u309f', '\u309f'), + ('\u30a1', '\u30fa'), ('\u30ff', '\u318e'), + ('\u31a0', '\u31ba'), ('\u31f0', '\u31ff'), + ('\u3400', '\u4db5'), ('\u4e00', '\ua014'), + ('\ua016', '\ua48c'), ('\ua4d0', '\ua4f7'), + ('\ua500', '\ua60b'), ('\ua610', '\ua61f'), + ('\ua62a', '\ua62b'), ('\ua66e', '\ua66e'), + ('\ua6a0', '\ua6e5'), ('\ua7fb', '\ua801'), + ('\ua803', '\ua805'), ('\ua807', '\ua80a'), + ('\ua80c', '\ua822'), ('\ua840', '\ua873'), + ('\ua882', '\ua8b3'), ('\ua8f2', '\ua8f7'), + ('\ua8fb', '\ua8fb'), ('\ua90a', '\ua925'), + ('\ua930', '\ua946'), ('\ua960', '\ua97c'), + ('\ua984', '\ua9b2'), ('\uaa00', '\uaa28'), + ('\uaa40', '\uaa42'), ('\uaa44', '\uaa4b'), + ('\uaa60', '\uaa6f'), ('\uaa71', '\uaa76'), + ('\uaa7a', '\uaa7a'), ('\uaa80', '\uaaaf'), + ('\uaab1', '\uaab1'), ('\uaab5', '\uaab6'), + ('\uaab9', '\uaabd'), ('\uaac0', '\uaac0'), + ('\uaac2', '\uaadc'), ('\uaae0', '\uaaea'), + ('\uaaf2', '\uaaf2'), ('\uab01', '\uabe2'), + ('\uac00', '\ud7fb'), ('\uf900', '\ufad9'), + ('\ufb1d', '\ufb1d'), ('\ufb1f', '\ufb28'), + ('\ufb2a', '\ufbb1'), ('\ufbd3', '\ufd3d'), + ('\ufd50', '\ufdfb'), ('\ufe70', '\ufefc'), + ('\uff66', '\uff6f'), ('\uff71', '\uff9d'), + ('\uffa0', '\uffdc'), ('\U00010000', '\U000100fa'), + ('\U00010280', '\U0001031e'), ('\U00010330', '\U00010340'), + ('\U00010342', '\U00010349'), ('\U00010380', '\U0001039d'), + ('\U000103a0', '\U000103cf'), ('\U00010450', '\U0001049d'), + ('\U00010800', '\U00010855'), ('\U00010900', '\U00010915'), + ('\U00010920', '\U00010939'), ('\U00010980', '\U00010a00'), + ('\U00010a10', '\U00010a33'), ('\U00010a60', '\U00010a7c'), + ('\U00010b00', '\U00010b35'), ('\U00010b40', '\U00010b55'), + ('\U00010b60', '\U00010b72'), ('\U00010c00', '\U00010c48'), + ('\U00011003', '\U00011037'), ('\U00011083', '\U000110af'), + ('\U000110d0', '\U000110e8'), ('\U00011103', '\U00011126'), + ('\U00011183', '\U000111b2'), ('\U000111c1', '\U000111c4'), + ('\U00011680', '\U000116aa'), ('\U00012000', '\U0001236e'), + ('\U00013000', '\U00016f50'), ('\U0001b000', '\U0001b001'), + ('\U0001ee00', '\U0001eebb'), ('\U00020000', '\U0002fa1d') + ]; + + pub fn Lo(c: char) -> bool { + bsearch_range_table(c, Lo_table) + } + + static Lt_table : &'static [(char,char)] = &[ + ('\u01c5', '\u01c5'), ('\u01c8', '\u01c8'), + ('\u01cb', '\u01cb'), ('\u01f2', '\u01f2'), + ('\u1f88', '\u1f8f'), ('\u1f98', '\u1f9f'), + ('\u1fa8', '\u1faf'), ('\u1fbc', '\u1fbc'), + ('\u1fcc', '\u1fcc'), ('\u1ffc', '\u1ffc') + ]; + + pub fn Lt(c: char) -> bool { + bsearch_range_table(c, Lt_table) + } + + static Lu_table : &'static [(char,char)] = &[ + ('\x41', '\x5a'), ('\xc0', '\xd6'), + ('\xd8', '\xde'), ('\u0100', '\u0100'), + ('\u0102', '\u0102'), ('\u0104', '\u0104'), + ('\u0106', '\u0106'), ('\u0108', '\u0108'), + ('\u010a', '\u010a'), ('\u010c', '\u010c'), + ('\u010e', '\u010e'), ('\u0110', '\u0110'), + ('\u0112', '\u0112'), ('\u0114', '\u0114'), + ('\u0116', '\u0116'), ('\u0118', '\u0118'), + ('\u011a', '\u011a'), ('\u011c', '\u011c'), + ('\u011e', '\u011e'), ('\u0120', '\u0120'), + ('\u0122', '\u0122'), ('\u0124', '\u0124'), + ('\u0126', '\u0126'), ('\u0128', '\u0128'), + ('\u012a', '\u012a'), ('\u012c', '\u012c'), + ('\u012e', '\u012e'), ('\u0130', '\u0130'), + ('\u0132', '\u0132'), ('\u0134', '\u0134'), + ('\u0136', '\u0136'), ('\u0139', '\u0139'), + ('\u013b', '\u013b'), ('\u013d', '\u013d'), + ('\u013f', '\u013f'), ('\u0141', '\u0141'), + ('\u0143', '\u0143'), ('\u0145', '\u0145'), + ('\u0147', '\u0147'), ('\u014a', '\u014a'), + ('\u014c', '\u014c'), ('\u014e', '\u014e'), + ('\u0150', '\u0150'), ('\u0152', '\u0152'), + ('\u0154', '\u0154'), ('\u0156', '\u0156'), + ('\u0158', '\u0158'), ('\u015a', '\u015a'), + ('\u015c', '\u015c'), ('\u015e', '\u015e'), + ('\u0160', '\u0160'), ('\u0162', '\u0162'), + ('\u0164', '\u0164'), ('\u0166', '\u0166'), + ('\u0168', '\u0168'), ('\u016a', '\u016a'), + ('\u016c', '\u016c'), ('\u016e', '\u016e'), + ('\u0170', '\u0170'), ('\u0172', '\u0172'), + ('\u0174', '\u0174'), ('\u0176', '\u0176'), + ('\u0178', '\u0179'), ('\u017b', '\u017b'), + ('\u017d', '\u017d'), ('\u0181', '\u0182'), + ('\u0184', '\u0184'), ('\u0186', '\u0187'), + ('\u0189', '\u018b'), ('\u018e', '\u0191'), + ('\u0193', '\u0194'), ('\u0196', '\u0198'), + ('\u019c', '\u019d'), ('\u019f', '\u01a0'), + ('\u01a2', '\u01a2'), ('\u01a4', '\u01a4'), + ('\u01a6', '\u01a7'), ('\u01a9', '\u01a9'), + ('\u01ac', '\u01ac'), ('\u01ae', '\u01af'), + ('\u01b1', '\u01b3'), ('\u01b5', '\u01b5'), + ('\u01b7', '\u01b8'), ('\u01bc', '\u01bc'), + ('\u01c4', '\u01c4'), ('\u01c7', '\u01c7'), + ('\u01ca', '\u01ca'), ('\u01cd', '\u01cd'), + ('\u01cf', '\u01cf'), ('\u01d1', '\u01d1'), + ('\u01d3', '\u01d3'), ('\u01d5', '\u01d5'), + ('\u01d7', '\u01d7'), ('\u01d9', '\u01d9'), + ('\u01db', '\u01db'), ('\u01de', '\u01de'), + ('\u01e0', '\u01e0'), ('\u01e2', '\u01e2'), + ('\u01e4', '\u01e4'), ('\u01e6', '\u01e6'), + ('\u01e8', '\u01e8'), ('\u01ea', '\u01ea'), + ('\u01ec', '\u01ec'), ('\u01ee', '\u01ee'), + ('\u01f1', '\u01f1'), ('\u01f4', '\u01f4'), + ('\u01f6', '\u01f8'), ('\u01fa', '\u01fa'), + ('\u01fc', '\u01fc'), ('\u01fe', '\u01fe'), + ('\u0200', '\u0200'), ('\u0202', '\u0202'), + ('\u0204', '\u0204'), ('\u0206', '\u0206'), + ('\u0208', '\u0208'), ('\u020a', '\u020a'), + ('\u020c', '\u020c'), ('\u020e', '\u020e'), + ('\u0210', '\u0210'), ('\u0212', '\u0212'), + ('\u0214', '\u0214'), ('\u0216', '\u0216'), + ('\u0218', '\u0218'), ('\u021a', '\u021a'), + ('\u021c', '\u021c'), ('\u021e', '\u021e'), + ('\u0220', '\u0220'), ('\u0222', '\u0222'), + ('\u0224', '\u0224'), ('\u0226', '\u0226'), + ('\u0228', '\u0228'), ('\u022a', '\u022a'), + ('\u022c', '\u022c'), ('\u022e', '\u022e'), + ('\u0230', '\u0230'), ('\u0232', '\u0232'), + ('\u023a', '\u023b'), ('\u023d', '\u023e'), + ('\u0241', '\u0241'), ('\u0243', '\u0246'), + ('\u0248', '\u0248'), ('\u024a', '\u024a'), + ('\u024c', '\u024c'), ('\u024e', '\u024e'), + ('\u0370', '\u0370'), ('\u0372', '\u0372'), + ('\u0376', '\u0376'), ('\u0386', '\u0386'), + ('\u0388', '\u038f'), ('\u0391', '\u03ab'), + ('\u03cf', '\u03cf'), ('\u03d2', '\u03d4'), + ('\u03d8', '\u03d8'), ('\u03da', '\u03da'), + ('\u03dc', '\u03dc'), ('\u03de', '\u03de'), + ('\u03e0', '\u03e0'), ('\u03e2', '\u03e2'), + ('\u03e4', '\u03e4'), ('\u03e6', '\u03e6'), + ('\u03e8', '\u03e8'), ('\u03ea', '\u03ea'), + ('\u03ec', '\u03ec'), ('\u03ee', '\u03ee'), + ('\u03f4', '\u03f4'), ('\u03f7', '\u03f7'), + ('\u03f9', '\u03fa'), ('\u03fd', '\u042f'), + ('\u0460', '\u0460'), ('\u0462', '\u0462'), + ('\u0464', '\u0464'), ('\u0466', '\u0466'), + ('\u0468', '\u0468'), ('\u046a', '\u046a'), + ('\u046c', '\u046c'), ('\u046e', '\u046e'), + ('\u0470', '\u0470'), ('\u0472', '\u0472'), + ('\u0474', '\u0474'), ('\u0476', '\u0476'), + ('\u0478', '\u0478'), ('\u047a', '\u047a'), + ('\u047c', '\u047c'), ('\u047e', '\u047e'), + ('\u0480', '\u0480'), ('\u048a', '\u048a'), + ('\u048c', '\u048c'), ('\u048e', '\u048e'), + ('\u0490', '\u0490'), ('\u0492', '\u0492'), + ('\u0494', '\u0494'), ('\u0496', '\u0496'), + ('\u0498', '\u0498'), ('\u049a', '\u049a'), + ('\u049c', '\u049c'), ('\u049e', '\u049e'), + ('\u04a0', '\u04a0'), ('\u04a2', '\u04a2'), + ('\u04a4', '\u04a4'), ('\u04a6', '\u04a6'), + ('\u04a8', '\u04a8'), ('\u04aa', '\u04aa'), + ('\u04ac', '\u04ac'), ('\u04ae', '\u04ae'), + ('\u04b0', '\u04b0'), ('\u04b2', '\u04b2'), + ('\u04b4', '\u04b4'), ('\u04b6', '\u04b6'), + ('\u04b8', '\u04b8'), ('\u04ba', '\u04ba'), + ('\u04bc', '\u04bc'), ('\u04be', '\u04be'), + ('\u04c0', '\u04c1'), ('\u04c3', '\u04c3'), + ('\u04c5', '\u04c5'), ('\u04c7', '\u04c7'), + ('\u04c9', '\u04c9'), ('\u04cb', '\u04cb'), + ('\u04cd', '\u04cd'), ('\u04d0', '\u04d0'), + ('\u04d2', '\u04d2'), ('\u04d4', '\u04d4'), + ('\u04d6', '\u04d6'), ('\u04d8', '\u04d8'), + ('\u04da', '\u04da'), ('\u04dc', '\u04dc'), + ('\u04de', '\u04de'), ('\u04e0', '\u04e0'), + ('\u04e2', '\u04e2'), ('\u04e4', '\u04e4'), + ('\u04e6', '\u04e6'), ('\u04e8', '\u04e8'), + ('\u04ea', '\u04ea'), ('\u04ec', '\u04ec'), + ('\u04ee', '\u04ee'), ('\u04f0', '\u04f0'), + ('\u04f2', '\u04f2'), ('\u04f4', '\u04f4'), + ('\u04f6', '\u04f6'), ('\u04f8', '\u04f8'), + ('\u04fa', '\u04fa'), ('\u04fc', '\u04fc'), + ('\u04fe', '\u04fe'), ('\u0500', '\u0500'), + ('\u0502', '\u0502'), ('\u0504', '\u0504'), + ('\u0506', '\u0506'), ('\u0508', '\u0508'), + ('\u050a', '\u050a'), ('\u050c', '\u050c'), + ('\u050e', '\u050e'), ('\u0510', '\u0510'), + ('\u0512', '\u0512'), ('\u0514', '\u0514'), + ('\u0516', '\u0516'), ('\u0518', '\u0518'), + ('\u051a', '\u051a'), ('\u051c', '\u051c'), + ('\u051e', '\u051e'), ('\u0520', '\u0520'), + ('\u0522', '\u0522'), ('\u0524', '\u0524'), + ('\u0526', '\u0526'), ('\u0531', '\u0556'), + ('\u10a0', '\u10cd'), ('\u1e00', '\u1e00'), + ('\u1e02', '\u1e02'), ('\u1e04', '\u1e04'), + ('\u1e06', '\u1e06'), ('\u1e08', '\u1e08'), + ('\u1e0a', '\u1e0a'), ('\u1e0c', '\u1e0c'), + ('\u1e0e', '\u1e0e'), ('\u1e10', '\u1e10'), + ('\u1e12', '\u1e12'), ('\u1e14', '\u1e14'), + ('\u1e16', '\u1e16'), ('\u1e18', '\u1e18'), + ('\u1e1a', '\u1e1a'), ('\u1e1c', '\u1e1c'), + ('\u1e1e', '\u1e1e'), ('\u1e20', '\u1e20'), + ('\u1e22', '\u1e22'), ('\u1e24', '\u1e24'), + ('\u1e26', '\u1e26'), ('\u1e28', '\u1e28'), + ('\u1e2a', '\u1e2a'), ('\u1e2c', '\u1e2c'), + ('\u1e2e', '\u1e2e'), ('\u1e30', '\u1e30'), + ('\u1e32', '\u1e32'), ('\u1e34', '\u1e34'), + ('\u1e36', '\u1e36'), ('\u1e38', '\u1e38'), + ('\u1e3a', '\u1e3a'), ('\u1e3c', '\u1e3c'), + ('\u1e3e', '\u1e3e'), ('\u1e40', '\u1e40'), + ('\u1e42', '\u1e42'), ('\u1e44', '\u1e44'), + ('\u1e46', '\u1e46'), ('\u1e48', '\u1e48'), + ('\u1e4a', '\u1e4a'), ('\u1e4c', '\u1e4c'), + ('\u1e4e', '\u1e4e'), ('\u1e50', '\u1e50'), + ('\u1e52', '\u1e52'), ('\u1e54', '\u1e54'), + ('\u1e56', '\u1e56'), ('\u1e58', '\u1e58'), + ('\u1e5a', '\u1e5a'), ('\u1e5c', '\u1e5c'), + ('\u1e5e', '\u1e5e'), ('\u1e60', '\u1e60'), + ('\u1e62', '\u1e62'), ('\u1e64', '\u1e64'), + ('\u1e66', '\u1e66'), ('\u1e68', '\u1e68'), + ('\u1e6a', '\u1e6a'), ('\u1e6c', '\u1e6c'), + ('\u1e6e', '\u1e6e'), ('\u1e70', '\u1e70'), + ('\u1e72', '\u1e72'), ('\u1e74', '\u1e74'), + ('\u1e76', '\u1e76'), ('\u1e78', '\u1e78'), + ('\u1e7a', '\u1e7a'), ('\u1e7c', '\u1e7c'), + ('\u1e7e', '\u1e7e'), ('\u1e80', '\u1e80'), + ('\u1e82', '\u1e82'), ('\u1e84', '\u1e84'), + ('\u1e86', '\u1e86'), ('\u1e88', '\u1e88'), + ('\u1e8a', '\u1e8a'), ('\u1e8c', '\u1e8c'), + ('\u1e8e', '\u1e8e'), ('\u1e90', '\u1e90'), + ('\u1e92', '\u1e92'), ('\u1e94', '\u1e94'), + ('\u1e9e', '\u1e9e'), ('\u1ea0', '\u1ea0'), + ('\u1ea2', '\u1ea2'), ('\u1ea4', '\u1ea4'), + ('\u1ea6', '\u1ea6'), ('\u1ea8', '\u1ea8'), + ('\u1eaa', '\u1eaa'), ('\u1eac', '\u1eac'), + ('\u1eae', '\u1eae'), ('\u1eb0', '\u1eb0'), + ('\u1eb2', '\u1eb2'), ('\u1eb4', '\u1eb4'), + ('\u1eb6', '\u1eb6'), ('\u1eb8', '\u1eb8'), + ('\u1eba', '\u1eba'), ('\u1ebc', '\u1ebc'), + ('\u1ebe', '\u1ebe'), ('\u1ec0', '\u1ec0'), + ('\u1ec2', '\u1ec2'), ('\u1ec4', '\u1ec4'), + ('\u1ec6', '\u1ec6'), ('\u1ec8', '\u1ec8'), + ('\u1eca', '\u1eca'), ('\u1ecc', '\u1ecc'), + ('\u1ece', '\u1ece'), ('\u1ed0', '\u1ed0'), + ('\u1ed2', '\u1ed2'), ('\u1ed4', '\u1ed4'), + ('\u1ed6', '\u1ed6'), ('\u1ed8', '\u1ed8'), + ('\u1eda', '\u1eda'), ('\u1edc', '\u1edc'), + ('\u1ede', '\u1ede'), ('\u1ee0', '\u1ee0'), + ('\u1ee2', '\u1ee2'), ('\u1ee4', '\u1ee4'), + ('\u1ee6', '\u1ee6'), ('\u1ee8', '\u1ee8'), + ('\u1eea', '\u1eea'), ('\u1eec', '\u1eec'), + ('\u1eee', '\u1eee'), ('\u1ef0', '\u1ef0'), + ('\u1ef2', '\u1ef2'), ('\u1ef4', '\u1ef4'), + ('\u1ef6', '\u1ef6'), ('\u1ef8', '\u1ef8'), + ('\u1efa', '\u1efa'), ('\u1efc', '\u1efc'), + ('\u1efe', '\u1efe'), ('\u1f08', '\u1f0f'), + ('\u1f18', '\u1f1d'), ('\u1f28', '\u1f2f'), + ('\u1f38', '\u1f3f'), ('\u1f48', '\u1f4d'), + ('\u1f59', '\u1f5f'), ('\u1f68', '\u1f6f'), + ('\u1fb8', '\u1fbb'), ('\u1fc8', '\u1fcb'), + ('\u1fd8', '\u1fdb'), ('\u1fe8', '\u1fec'), + ('\u1ff8', '\u1ffb'), ('\u2102', '\u2102'), + ('\u2107', '\u2107'), ('\u210b', '\u210d'), + ('\u2110', '\u2112'), ('\u2115', '\u2115'), + ('\u2119', '\u211d'), ('\u2124', '\u2124'), + ('\u2126', '\u2126'), ('\u2128', '\u2128'), + ('\u212a', '\u212d'), ('\u2130', '\u2133'), + ('\u213e', '\u213f'), ('\u2145', '\u2145'), + ('\u2183', '\u2183'), ('\u2c00', '\u2c2e'), + ('\u2c60', '\u2c60'), ('\u2c62', '\u2c64'), + ('\u2c67', '\u2c67'), ('\u2c69', '\u2c69'), + ('\u2c6b', '\u2c6b'), ('\u2c6d', '\u2c70'), + ('\u2c72', '\u2c72'), ('\u2c75', '\u2c75'), + ('\u2c7e', '\u2c80'), ('\u2c82', '\u2c82'), + ('\u2c84', '\u2c84'), ('\u2c86', '\u2c86'), + ('\u2c88', '\u2c88'), ('\u2c8a', '\u2c8a'), + ('\u2c8c', '\u2c8c'), ('\u2c8e', '\u2c8e'), + ('\u2c90', '\u2c90'), ('\u2c92', '\u2c92'), + ('\u2c94', '\u2c94'), ('\u2c96', '\u2c96'), + ('\u2c98', '\u2c98'), ('\u2c9a', '\u2c9a'), + ('\u2c9c', '\u2c9c'), ('\u2c9e', '\u2c9e'), + ('\u2ca0', '\u2ca0'), ('\u2ca2', '\u2ca2'), + ('\u2ca4', '\u2ca4'), ('\u2ca6', '\u2ca6'), + ('\u2ca8', '\u2ca8'), ('\u2caa', '\u2caa'), + ('\u2cac', '\u2cac'), ('\u2cae', '\u2cae'), + ('\u2cb0', '\u2cb0'), ('\u2cb2', '\u2cb2'), + ('\u2cb4', '\u2cb4'), ('\u2cb6', '\u2cb6'), + ('\u2cb8', '\u2cb8'), ('\u2cba', '\u2cba'), + ('\u2cbc', '\u2cbc'), ('\u2cbe', '\u2cbe'), + ('\u2cc0', '\u2cc0'), ('\u2cc2', '\u2cc2'), + ('\u2cc4', '\u2cc4'), ('\u2cc6', '\u2cc6'), + ('\u2cc8', '\u2cc8'), ('\u2cca', '\u2cca'), + ('\u2ccc', '\u2ccc'), ('\u2cce', '\u2cce'), + ('\u2cd0', '\u2cd0'), ('\u2cd2', '\u2cd2'), + ('\u2cd4', '\u2cd4'), ('\u2cd6', '\u2cd6'), + ('\u2cd8', '\u2cd8'), ('\u2cda', '\u2cda'), + ('\u2cdc', '\u2cdc'), ('\u2cde', '\u2cde'), + ('\u2ce0', '\u2ce0'), ('\u2ce2', '\u2ce2'), + ('\u2ceb', '\u2ceb'), ('\u2ced', '\u2ced'), + ('\u2cf2', '\u2cf2'), ('\ua640', '\ua640'), + ('\ua642', '\ua642'), ('\ua644', '\ua644'), + ('\ua646', '\ua646'), ('\ua648', '\ua648'), + ('\ua64a', '\ua64a'), ('\ua64c', '\ua64c'), + ('\ua64e', '\ua64e'), ('\ua650', '\ua650'), + ('\ua652', '\ua652'), ('\ua654', '\ua654'), + ('\ua656', '\ua656'), ('\ua658', '\ua658'), + ('\ua65a', '\ua65a'), ('\ua65c', '\ua65c'), + ('\ua65e', '\ua65e'), ('\ua660', '\ua660'), + ('\ua662', '\ua662'), ('\ua664', '\ua664'), + ('\ua666', '\ua666'), ('\ua668', '\ua668'), + ('\ua66a', '\ua66a'), ('\ua66c', '\ua66c'), + ('\ua680', '\ua680'), ('\ua682', '\ua682'), + ('\ua684', '\ua684'), ('\ua686', '\ua686'), + ('\ua688', '\ua688'), ('\ua68a', '\ua68a'), + ('\ua68c', '\ua68c'), ('\ua68e', '\ua68e'), + ('\ua690', '\ua690'), ('\ua692', '\ua692'), + ('\ua694', '\ua694'), ('\ua696', '\ua696'), + ('\ua722', '\ua722'), ('\ua724', '\ua724'), + ('\ua726', '\ua726'), ('\ua728', '\ua728'), + ('\ua72a', '\ua72a'), ('\ua72c', '\ua72c'), + ('\ua72e', '\ua72e'), ('\ua732', '\ua732'), + ('\ua734', '\ua734'), ('\ua736', '\ua736'), + ('\ua738', '\ua738'), ('\ua73a', '\ua73a'), + ('\ua73c', '\ua73c'), ('\ua73e', '\ua73e'), + ('\ua740', '\ua740'), ('\ua742', '\ua742'), + ('\ua744', '\ua744'), ('\ua746', '\ua746'), + ('\ua748', '\ua748'), ('\ua74a', '\ua74a'), + ('\ua74c', '\ua74c'), ('\ua74e', '\ua74e'), + ('\ua750', '\ua750'), ('\ua752', '\ua752'), + ('\ua754', '\ua754'), ('\ua756', '\ua756'), + ('\ua758', '\ua758'), ('\ua75a', '\ua75a'), + ('\ua75c', '\ua75c'), ('\ua75e', '\ua75e'), + ('\ua760', '\ua760'), ('\ua762', '\ua762'), + ('\ua764', '\ua764'), ('\ua766', '\ua766'), + ('\ua768', '\ua768'), ('\ua76a', '\ua76a'), + ('\ua76c', '\ua76c'), ('\ua76e', '\ua76e'), + ('\ua779', '\ua779'), ('\ua77b', '\ua77b'), + ('\ua77d', '\ua77e'), ('\ua780', '\ua780'), + ('\ua782', '\ua782'), ('\ua784', '\ua784'), + ('\ua786', '\ua786'), ('\ua78b', '\ua78b'), + ('\ua78d', '\ua78d'), ('\ua790', '\ua790'), + ('\ua792', '\ua792'), ('\ua7a0', '\ua7a0'), + ('\ua7a2', '\ua7a2'), ('\ua7a4', '\ua7a4'), + ('\ua7a6', '\ua7a6'), ('\ua7a8', '\ua7a8'), + ('\ua7aa', '\ua7aa'), ('\uff21', '\uff3a'), + ('\U00010400', '\U00010427'), ('\U0001d400', '\U0001d419'), + ('\U0001d434', '\U0001d44d'), ('\U0001d468', '\U0001d481'), + ('\U0001d49c', '\U0001d4b5'), ('\U0001d4d0', '\U0001d4e9'), + ('\U0001d504', '\U0001d51c'), ('\U0001d538', '\U0001d550'), + ('\U0001d56c', '\U0001d585'), ('\U0001d5a0', '\U0001d5b9'), + ('\U0001d5d4', '\U0001d5ed'), ('\U0001d608', '\U0001d621'), + ('\U0001d63c', '\U0001d655'), ('\U0001d670', '\U0001d689'), + ('\U0001d6a8', '\U0001d6c0'), ('\U0001d6e2', '\U0001d6fa'), + ('\U0001d71c', '\U0001d734'), ('\U0001d756', '\U0001d76e'), + ('\U0001d790', '\U0001d7a8'), ('\U0001d7ca', '\U0001d7ca') + ]; + + pub fn Lu(c: char) -> bool { + bsearch_range_table(c, Lu_table) + } + + static Mc_table : &'static [(char,char)] = &[ + ('\u0903', '\u0903'), ('\u093b', '\u093b'), + ('\u093e', '\u0940'), ('\u0949', '\u094c'), + ('\u094e', '\u094f'), ('\u0982', '\u0983'), + ('\u09be', '\u09c0'), ('\u09c7', '\u09cc'), + ('\u09d7', '\u09d7'), ('\u0a03', '\u0a03'), + ('\u0a3e', '\u0a40'), ('\u0a83', '\u0a83'), + ('\u0abe', '\u0ac0'), ('\u0ac9', '\u0acc'), + ('\u0b02', '\u0b03'), ('\u0b3e', '\u0b3e'), + ('\u0b40', '\u0b40'), ('\u0b47', '\u0b4c'), + ('\u0b57', '\u0b57'), ('\u0bbe', '\u0bbf'), + ('\u0bc1', '\u0bcc'), ('\u0bd7', '\u0bd7'), + ('\u0c01', '\u0c03'), ('\u0c41', '\u0c44'), + ('\u0c82', '\u0c83'), ('\u0cbe', '\u0cbe'), + ('\u0cc0', '\u0cc4'), ('\u0cc7', '\u0ccb'), + ('\u0cd5', '\u0cd6'), ('\u0d02', '\u0d03'), + ('\u0d3e', '\u0d40'), ('\u0d46', '\u0d4c'), + ('\u0d57', '\u0d57'), ('\u0d82', '\u0d83'), + ('\u0dcf', '\u0dd1'), ('\u0dd8', '\u0df3'), + ('\u0f3e', '\u0f3f'), ('\u0f7f', '\u0f7f'), + ('\u102b', '\u102c'), ('\u1031', '\u1031'), + ('\u1038', '\u1038'), ('\u103b', '\u103c'), + ('\u1056', '\u1057'), ('\u1062', '\u1064'), + ('\u1067', '\u106d'), ('\u1083', '\u1084'), + ('\u1087', '\u108c'), ('\u108f', '\u108f'), + ('\u109a', '\u109c'), ('\u17b6', '\u17b6'), + ('\u17be', '\u17c5'), ('\u17c7', '\u17c8'), + ('\u1923', '\u1926'), ('\u1929', '\u1931'), + ('\u1933', '\u1938'), ('\u19b0', '\u19c0'), + ('\u19c8', '\u19c9'), ('\u1a19', '\u1a1b'), + ('\u1a55', '\u1a55'), ('\u1a57', '\u1a57'), + ('\u1a61', '\u1a61'), ('\u1a63', '\u1a64'), + ('\u1a6d', '\u1a72'), ('\u1b04', '\u1b04'), + ('\u1b35', '\u1b35'), ('\u1b3b', '\u1b3b'), + ('\u1b3d', '\u1b41'), ('\u1b43', '\u1b44'), + ('\u1b82', '\u1b82'), ('\u1ba1', '\u1ba1'), + ('\u1ba6', '\u1ba7'), ('\u1baa', '\u1baa'), + ('\u1bac', '\u1bad'), ('\u1be7', '\u1be7'), + ('\u1bea', '\u1bec'), ('\u1bee', '\u1bee'), + ('\u1bf2', '\u1bf3'), ('\u1c24', '\u1c2b'), + ('\u1c34', '\u1c35'), ('\u1ce1', '\u1ce1'), + ('\u1cf2', '\u1cf3'), ('\u302e', '\u302f'), + ('\ua823', '\ua824'), ('\ua827', '\ua827'), + ('\ua880', '\ua881'), ('\ua8b4', '\ua8c3'), + ('\ua952', '\ua953'), ('\ua983', '\ua983'), + ('\ua9b4', '\ua9b5'), ('\ua9ba', '\ua9bb'), + ('\ua9bd', '\ua9c0'), ('\uaa2f', '\uaa30'), + ('\uaa33', '\uaa34'), ('\uaa4d', '\uaa4d'), + ('\uaa7b', '\uaa7b'), ('\uaaeb', '\uaaeb'), + ('\uaaee', '\uaaef'), ('\uaaf5', '\uaaf5'), + ('\uabe3', '\uabe4'), ('\uabe6', '\uabe7'), + ('\uabe9', '\uabea'), ('\uabec', '\uabec'), + ('\U00011000', '\U00011000'), ('\U00011002', '\U00011002'), + ('\U00011082', '\U00011082'), ('\U000110b0', '\U000110b2'), + ('\U000110b7', '\U000110b8'), ('\U0001112c', '\U0001112c'), + ('\U00011182', '\U00011182'), ('\U000111b3', '\U000111b5'), + ('\U000111bf', '\U000111c0'), ('\U000116ac', '\U000116ac'), + ('\U000116ae', '\U000116af'), ('\U000116b6', '\U000116b6'), + ('\U00016f51', '\U00016f7e'), ('\U0001d165', '\U0001d166'), + ('\U0001d16d', '\U0001d172') + ]; + + pub fn Mc(c: char) -> bool { + bsearch_range_table(c, Mc_table) + } + + static Me_table : &'static [(char,char)] = &[ + ('\u0488', '\u0489'), ('\u20dd', '\u20e0'), + ('\u20e2', '\u20e4'), ('\ua670', '\ua672') + ]; + + pub fn Me(c: char) -> bool { + bsearch_range_table(c, Me_table) + } + + static Mn_table : &'static [(char,char)] = &[ + ('\u0300', '\u036f'), ('\u0483', '\u0487'), + ('\u0591', '\u05bd'), ('\u05bf', '\u05bf'), + ('\u05c1', '\u05c2'), ('\u05c4', '\u05c5'), + ('\u05c7', '\u05c7'), ('\u0610', '\u061a'), + ('\u064b', '\u065f'), ('\u0670', '\u0670'), + ('\u06d6', '\u06dc'), ('\u06df', '\u06e4'), + ('\u06e7', '\u06e8'), ('\u06ea', '\u06ed'), + ('\u0711', '\u0711'), ('\u0730', '\u074a'), + ('\u07a6', '\u07b0'), ('\u07eb', '\u07f3'), + ('\u0816', '\u0819'), ('\u081b', '\u0823'), + ('\u0825', '\u0827'), ('\u0829', '\u082d'), + ('\u0859', '\u085b'), ('\u08e4', '\u0902'), + ('\u093a', '\u093a'), ('\u093c', '\u093c'), + ('\u0941', '\u0948'), ('\u094d', '\u094d'), + ('\u0951', '\u0957'), ('\u0962', '\u0963'), + ('\u0981', '\u0981'), ('\u09bc', '\u09bc'), + ('\u09c1', '\u09c4'), ('\u09cd', '\u09cd'), + ('\u09e2', '\u09e3'), ('\u0a01', '\u0a02'), + ('\u0a3c', '\u0a3c'), ('\u0a41', '\u0a51'), + ('\u0a70', '\u0a71'), ('\u0a75', '\u0a82'), + ('\u0abc', '\u0abc'), ('\u0ac1', '\u0ac8'), + ('\u0acd', '\u0acd'), ('\u0ae2', '\u0ae3'), + ('\u0b01', '\u0b01'), ('\u0b3c', '\u0b3c'), + ('\u0b3f', '\u0b3f'), ('\u0b41', '\u0b44'), + ('\u0b4d', '\u0b56'), ('\u0b62', '\u0b63'), + ('\u0b82', '\u0b82'), ('\u0bc0', '\u0bc0'), + ('\u0bcd', '\u0bcd'), ('\u0c3e', '\u0c40'), + ('\u0c46', '\u0c56'), ('\u0c62', '\u0c63'), + ('\u0cbc', '\u0cbc'), ('\u0cbf', '\u0cbf'), + ('\u0cc6', '\u0cc6'), ('\u0ccc', '\u0ccd'), + ('\u0ce2', '\u0ce3'), ('\u0d41', '\u0d44'), + ('\u0d4d', '\u0d4d'), ('\u0d62', '\u0d63'), + ('\u0dca', '\u0dca'), ('\u0dd2', '\u0dd6'), + ('\u0e31', '\u0e31'), ('\u0e34', '\u0e3a'), + ('\u0e47', '\u0e4e'), ('\u0eb1', '\u0eb1'), + ('\u0eb4', '\u0ebc'), ('\u0ec8', '\u0ecd'), + ('\u0f18', '\u0f19'), ('\u0f35', '\u0f35'), + ('\u0f37', '\u0f37'), ('\u0f39', '\u0f39'), + ('\u0f71', '\u0f7e'), ('\u0f80', '\u0f84'), + ('\u0f86', '\u0f87'), ('\u0f8d', '\u0fbc'), + ('\u0fc6', '\u0fc6'), ('\u102d', '\u1030'), + ('\u1032', '\u1037'), ('\u1039', '\u103a'), + ('\u103d', '\u103e'), ('\u1058', '\u1059'), + ('\u105e', '\u1060'), ('\u1071', '\u1074'), + ('\u1082', '\u1082'), ('\u1085', '\u1086'), + ('\u108d', '\u108d'), ('\u109d', '\u109d'), + ('\u135d', '\u135f'), ('\u1712', '\u1714'), + ('\u1732', '\u1734'), ('\u1752', '\u1753'), + ('\u1772', '\u1773'), ('\u17b4', '\u17b5'), + ('\u17b7', '\u17bd'), ('\u17c6', '\u17c6'), + ('\u17c9', '\u17d3'), ('\u17dd', '\u17dd'), + ('\u180b', '\u180d'), ('\u18a9', '\u18a9'), + ('\u1920', '\u1922'), ('\u1927', '\u1928'), + ('\u1932', '\u1932'), ('\u1939', '\u193b'), + ('\u1a17', '\u1a18'), ('\u1a56', '\u1a56'), + ('\u1a58', '\u1a60'), ('\u1a62', '\u1a62'), + ('\u1a65', '\u1a6c'), ('\u1a73', '\u1a7f'), + ('\u1b00', '\u1b03'), ('\u1b34', '\u1b34'), + ('\u1b36', '\u1b3a'), ('\u1b3c', '\u1b3c'), + ('\u1b42', '\u1b42'), ('\u1b6b', '\u1b73'), + ('\u1b80', '\u1b81'), ('\u1ba2', '\u1ba5'), + ('\u1ba8', '\u1ba9'), ('\u1bab', '\u1bab'), + ('\u1be6', '\u1be6'), ('\u1be8', '\u1be9'), + ('\u1bed', '\u1bed'), ('\u1bef', '\u1bf1'), + ('\u1c2c', '\u1c33'), ('\u1c36', '\u1c37'), + ('\u1cd0', '\u1cd2'), ('\u1cd4', '\u1ce0'), + ('\u1ce2', '\u1ce8'), ('\u1ced', '\u1ced'), + ('\u1cf4', '\u1cf4'), ('\u1dc0', '\u1dff'), + ('\u20d0', '\u20dc'), ('\u20e1', '\u20e1'), + ('\u20e5', '\u20f0'), ('\u2cef', '\u2cf1'), + ('\u2d7f', '\u2d7f'), ('\u2de0', '\u2dff'), + ('\u302a', '\u302d'), ('\u3099', '\u309a'), + ('\ua66f', '\ua66f'), ('\ua674', '\ua67d'), + ('\ua69f', '\ua69f'), ('\ua6f0', '\ua6f1'), + ('\ua802', '\ua802'), ('\ua806', '\ua806'), + ('\ua80b', '\ua80b'), ('\ua825', '\ua826'), + ('\ua8c4', '\ua8c4'), ('\ua8e0', '\ua8f1'), + ('\ua926', '\ua92d'), ('\ua947', '\ua951'), + ('\ua980', '\ua982'), ('\ua9b3', '\ua9b3'), + ('\ua9b6', '\ua9b9'), ('\ua9bc', '\ua9bc'), + ('\uaa29', '\uaa2e'), ('\uaa31', '\uaa32'), + ('\uaa35', '\uaa36'), ('\uaa43', '\uaa43'), + ('\uaa4c', '\uaa4c'), ('\uaab0', '\uaab0'), + ('\uaab2', '\uaab4'), ('\uaab7', '\uaab8'), + ('\uaabe', '\uaabf'), ('\uaac1', '\uaac1'), + ('\uaaec', '\uaaed'), ('\uaaf6', '\uaaf6'), + ('\uabe5', '\uabe5'), ('\uabe8', '\uabe8'), + ('\uabed', '\uabed'), ('\ufb1e', '\ufb1e'), + ('\ufe00', '\ufe0f'), ('\ufe20', '\ufe26'), + ('\U000101fd', '\U000101fd'), ('\U00010a01', '\U00010a0f'), + ('\U00010a38', '\U00010a3f'), ('\U00011001', '\U00011001'), + ('\U00011038', '\U00011046'), ('\U00011080', '\U00011081'), + ('\U000110b3', '\U000110b6'), ('\U000110b9', '\U000110ba'), + ('\U00011100', '\U00011102'), ('\U00011127', '\U0001112b'), + ('\U0001112d', '\U00011134'), ('\U00011180', '\U00011181'), + ('\U000111b6', '\U000111be'), ('\U000116ab', '\U000116ab'), + ('\U000116ad', '\U000116ad'), ('\U000116b0', '\U000116b5'), + ('\U000116b7', '\U000116b7'), ('\U00016f8f', '\U00016f92'), + ('\U0001d167', '\U0001d169'), ('\U0001d17b', '\U0001d182'), + ('\U0001d185', '\U0001d18b'), ('\U0001d1aa', '\U0001d1ad'), + ('\U0001d242', '\U0001d244'), ('\U000e0100', '\U000e01ef') + ]; + + pub fn Mn(c: char) -> bool { + bsearch_range_table(c, Mn_table) + } + + static Nd_table : &'static [(char,char)] = &[ + ('\x30', '\x39'), ('\u0660', '\u0669'), + ('\u06f0', '\u06f9'), ('\u07c0', '\u07c9'), + ('\u0966', '\u096f'), ('\u09e6', '\u09ef'), + ('\u0a66', '\u0a6f'), ('\u0ae6', '\u0aef'), + ('\u0b66', '\u0b6f'), ('\u0be6', '\u0bef'), + ('\u0c66', '\u0c6f'), ('\u0ce6', '\u0cef'), + ('\u0d66', '\u0d6f'), ('\u0e50', '\u0e59'), + ('\u0ed0', '\u0ed9'), ('\u0f20', '\u0f29'), + ('\u1040', '\u1049'), ('\u1090', '\u1099'), + ('\u17e0', '\u17e9'), ('\u1810', '\u1819'), + ('\u1946', '\u194f'), ('\u19d0', '\u19d9'), + ('\u1a80', '\u1a99'), ('\u1b50', '\u1b59'), + ('\u1bb0', '\u1bb9'), ('\u1c40', '\u1c49'), + ('\u1c50', '\u1c59'), ('\ua620', '\ua629'), + ('\ua8d0', '\ua8d9'), ('\ua900', '\ua909'), + ('\ua9d0', '\ua9d9'), ('\uaa50', '\uaa59'), + ('\uabf0', '\uabf9'), ('\uff10', '\uff19'), + ('\U000104a0', '\U000104a9'), ('\U00011066', '\U0001106f'), + ('\U000110f0', '\U000110f9'), ('\U00011136', '\U0001113f'), + ('\U000111d0', '\U000111d9'), ('\U000116c0', '\U000116c9'), + ('\U0001d7ce', '\U0001d7ff') + ]; + + pub fn Nd(c: char) -> bool { + bsearch_range_table(c, Nd_table) + } + + static Nl_table : &'static [(char,char)] = &[ + ('\u16ee', '\u16f0'), ('\u2160', '\u2182'), + ('\u2185', '\u2188'), ('\u3007', '\u3007'), + ('\u3021', '\u3029'), ('\u3038', '\u303a'), + ('\ua6e6', '\ua6ef'), ('\U00010140', '\U00010174'), + ('\U00010341', '\U00010341'), ('\U0001034a', '\U0001034a'), + ('\U000103d1', '\U000103d5'), ('\U00012400', '\U00012462') + ]; + + pub fn Nl(c: char) -> bool { + bsearch_range_table(c, Nl_table) + } + + static No_table : &'static [(char,char)] = &[ + ('\xb2', '\xb3'), ('\xb9', '\xb9'), + ('\xbc', '\xbe'), ('\u09f4', '\u09f9'), + ('\u0b72', '\u0b77'), ('\u0bf0', '\u0bf2'), + ('\u0c78', '\u0c7e'), ('\u0d70', '\u0d75'), + ('\u0f2a', '\u0f33'), ('\u1369', '\u137c'), + ('\u17f0', '\u17f9'), ('\u19da', '\u19da'), + ('\u2070', '\u2070'), ('\u2074', '\u2079'), + ('\u2080', '\u2089'), ('\u2150', '\u215f'), + ('\u2189', '\u2189'), ('\u2460', '\u249b'), + ('\u24ea', '\u24ff'), ('\u2776', '\u2793'), + ('\u2cfd', '\u2cfd'), ('\u3192', '\u3195'), + ('\u3220', '\u3229'), ('\u3248', '\u324f'), + ('\u3251', '\u325f'), ('\u3280', '\u3289'), + ('\u32b1', '\u32bf'), ('\ua830', '\ua835'), + ('\U00010107', '\U00010133'), ('\U00010175', '\U00010178'), + ('\U0001018a', '\U0001018a'), ('\U00010320', '\U00010323'), + ('\U00010858', '\U0001085f'), ('\U00010916', '\U0001091b'), + ('\U00010a40', '\U00010a47'), ('\U00010a7d', '\U00010a7e'), + ('\U00010b58', '\U00010b5f'), ('\U00010b78', '\U00010b7f'), + ('\U00010e60', '\U00010e7e'), ('\U00011052', '\U00011065'), + ('\U0001d360', '\U0001d371'), ('\U0001f100', '\U0001f10a') + ]; + + pub fn No(c: char) -> bool { + bsearch_range_table(c, No_table) + } -/* -Function: is_upper + static Pc_table : &'static [(char,char)] = &[ + ('\x5f', '\x5f'), ('\u203f', '\u2040'), + ('\u2054', '\u2054'), ('\ufe33', '\ufe34'), + ('\ufe4d', '\ufe4f'), ('\uff3f', '\uff3f') + ]; + + pub fn Pc(c: char) -> bool { + bsearch_range_table(c, Pc_table) + } + + static Pd_table : &'static [(char,char)] = &[ + ('\x2d', '\x2d'), ('\u058a', '\u058a'), + ('\u05be', '\u05be'), ('\u1400', '\u1400'), + ('\u1806', '\u1806'), ('\u2010', '\u2015'), + ('\u2e17', '\u2e17'), ('\u2e1a', '\u2e1a'), + ('\u2e3a', '\u2e3b'), ('\u301c', '\u301c'), + ('\u3030', '\u3030'), ('\u30a0', '\u30a0'), + ('\ufe31', '\ufe32'), ('\ufe58', '\ufe58'), + ('\ufe63', '\ufe63'), ('\uff0d', '\uff0d') + ]; + + pub fn Pd(c: char) -> bool { + bsearch_range_table(c, Pd_table) + } + + static Pe_table : &'static [(char,char)] = &[ + ('\x29', '\x29'), ('\x5d', '\x5d'), + ('\x7d', '\x7d'), ('\u0f3b', '\u0f3b'), + ('\u0f3d', '\u0f3d'), ('\u169c', '\u169c'), + ('\u2046', '\u2046'), ('\u207e', '\u207e'), + ('\u208e', '\u208e'), ('\u232a', '\u232a'), + ('\u2769', '\u2769'), ('\u276b', '\u276b'), + ('\u276d', '\u276d'), ('\u276f', '\u276f'), + ('\u2771', '\u2771'), ('\u2773', '\u2773'), + ('\u2775', '\u2775'), ('\u27c6', '\u27c6'), + ('\u27e7', '\u27e7'), ('\u27e9', '\u27e9'), + ('\u27eb', '\u27eb'), ('\u27ed', '\u27ed'), + ('\u27ef', '\u27ef'), ('\u2984', '\u2984'), + ('\u2986', '\u2986'), ('\u2988', '\u2988'), + ('\u298a', '\u298a'), ('\u298c', '\u298c'), + ('\u298e', '\u298e'), ('\u2990', '\u2990'), + ('\u2992', '\u2992'), ('\u2994', '\u2994'), + ('\u2996', '\u2996'), ('\u2998', '\u2998'), + ('\u29d9', '\u29d9'), ('\u29db', '\u29db'), + ('\u29fd', '\u29fd'), ('\u2e23', '\u2e23'), + ('\u2e25', '\u2e25'), ('\u2e27', '\u2e27'), + ('\u2e29', '\u2e29'), ('\u3009', '\u3009'), + ('\u300b', '\u300b'), ('\u300d', '\u300d'), + ('\u300f', '\u300f'), ('\u3011', '\u3011'), + ('\u3015', '\u3015'), ('\u3017', '\u3017'), + ('\u3019', '\u3019'), ('\u301b', '\u301b'), + ('\u301e', '\u301f'), ('\ufd3f', '\ufd3f'), + ('\ufe18', '\ufe18'), ('\ufe36', '\ufe36'), + ('\ufe38', '\ufe38'), ('\ufe3a', '\ufe3a'), + ('\ufe3c', '\ufe3c'), ('\ufe3e', '\ufe3e'), + ('\ufe40', '\ufe40'), ('\ufe42', '\ufe42'), + ('\ufe44', '\ufe44'), ('\ufe48', '\ufe48'), + ('\ufe5a', '\ufe5a'), ('\ufe5c', '\ufe5c'), + ('\ufe5e', '\ufe5e'), ('\uff09', '\uff09'), + ('\uff3d', '\uff3d'), ('\uff5d', '\uff5d'), + ('\uff60', '\uff60'), ('\uff63', '\uff63') + ]; + + pub fn Pe(c: char) -> bool { + bsearch_range_table(c, Pe_table) + } + + static Pf_table : &'static [(char,char)] = &[ + ('\xbb', '\xbb'), ('\u2019', '\u2019'), + ('\u201d', '\u201d'), ('\u203a', '\u203a'), + ('\u2e03', '\u2e03'), ('\u2e05', '\u2e05'), + ('\u2e0a', '\u2e0a'), ('\u2e0d', '\u2e0d'), + ('\u2e1d', '\u2e1d'), ('\u2e21', '\u2e21') + ]; + + pub fn Pf(c: char) -> bool { + bsearch_range_table(c, Pf_table) + } + + static Pi_table : &'static [(char,char)] = &[ + ('\xab', '\xab'), ('\u2018', '\u2018'), + ('\u201b', '\u201c'), ('\u201f', '\u201f'), + ('\u2039', '\u2039'), ('\u2e02', '\u2e02'), + ('\u2e04', '\u2e04'), ('\u2e09', '\u2e09'), + ('\u2e0c', '\u2e0c'), ('\u2e1c', '\u2e1c'), + ('\u2e20', '\u2e20') + ]; + + pub fn Pi(c: char) -> bool { + bsearch_range_table(c, Pi_table) + } + + static Po_table : &'static [(char,char)] = &[ + ('\x21', '\x23'), ('\x25', '\x27'), + ('\x2a', '\x2a'), ('\x2c', '\x2c'), + ('\x2e', '\x2f'), ('\x3a', '\x3b'), + ('\x3f', '\x40'), ('\x5c', '\x5c'), + ('\xa1', '\xa1'), ('\xa7', '\xa7'), + ('\xb6', '\xb7'), ('\xbf', '\xbf'), + ('\u037e', '\u037e'), ('\u0387', '\u0387'), + ('\u055a', '\u055f'), ('\u0589', '\u0589'), + ('\u05c0', '\u05c0'), ('\u05c3', '\u05c3'), + ('\u05c6', '\u05c6'), ('\u05f3', '\u05f4'), + ('\u0609', '\u060a'), ('\u060c', '\u060d'), + ('\u061b', '\u061f'), ('\u066a', '\u066d'), + ('\u06d4', '\u06d4'), ('\u0700', '\u070d'), + ('\u07f7', '\u07f9'), ('\u0830', '\u083e'), + ('\u085e', '\u085e'), ('\u0964', '\u0965'), + ('\u0970', '\u0970'), ('\u0af0', '\u0af0'), + ('\u0df4', '\u0df4'), ('\u0e4f', '\u0e4f'), + ('\u0e5a', '\u0e5b'), ('\u0f04', '\u0f12'), + ('\u0f14', '\u0f14'), ('\u0f85', '\u0f85'), + ('\u0fd0', '\u0fd4'), ('\u0fd9', '\u0fda'), + ('\u104a', '\u104f'), ('\u10fb', '\u10fb'), + ('\u1360', '\u1368'), ('\u166d', '\u166e'), + ('\u16eb', '\u16ed'), ('\u1735', '\u1736'), + ('\u17d4', '\u17d6'), ('\u17d8', '\u17da'), + ('\u1800', '\u1805'), ('\u1807', '\u180a'), + ('\u1944', '\u1945'), ('\u1a1e', '\u1a1f'), + ('\u1aa0', '\u1aa6'), ('\u1aa8', '\u1aad'), + ('\u1b5a', '\u1b60'), ('\u1bfc', '\u1bff'), + ('\u1c3b', '\u1c3f'), ('\u1c7e', '\u1cc7'), + ('\u1cd3', '\u1cd3'), ('\u2016', '\u2017'), + ('\u2020', '\u2027'), ('\u2030', '\u2038'), + ('\u203b', '\u203e'), ('\u2041', '\u2043'), + ('\u2047', '\u2051'), ('\u2053', '\u2053'), + ('\u2055', '\u205e'), ('\u2cf9', '\u2cfc'), + ('\u2cfe', '\u2cff'), ('\u2d70', '\u2d70'), + ('\u2e00', '\u2e01'), ('\u2e06', '\u2e08'), + ('\u2e0b', '\u2e0b'), ('\u2e0e', '\u2e16'), + ('\u2e18', '\u2e19'), ('\u2e1b', '\u2e1b'), + ('\u2e1e', '\u2e1f'), ('\u2e2a', '\u2e2e'), + ('\u2e30', '\u2e39'), ('\u3001', '\u3003'), + ('\u303d', '\u303d'), ('\u30fb', '\u30fb'), + ('\ua4fe', '\ua4ff'), ('\ua60d', '\ua60f'), + ('\ua673', '\ua673'), ('\ua67e', '\ua67e'), + ('\ua6f2', '\ua6f7'), ('\ua874', '\ua877'), + ('\ua8ce', '\ua8cf'), ('\ua8f8', '\ua8fa'), + ('\ua92e', '\ua92f'), ('\ua95f', '\ua95f'), + ('\ua9c1', '\ua9cd'), ('\ua9de', '\ua9df'), + ('\uaa5c', '\uaa5f'), ('\uaade', '\uaadf'), + ('\uaaf0', '\uaaf1'), ('\uabeb', '\uabeb'), + ('\ufe10', '\ufe16'), ('\ufe19', '\ufe19'), + ('\ufe30', '\ufe30'), ('\ufe45', '\ufe46'), + ('\ufe49', '\ufe4c'), ('\ufe50', '\ufe57'), + ('\ufe5f', '\ufe61'), ('\ufe68', '\ufe68'), + ('\ufe6a', '\ufe6b'), ('\uff01', '\uff03'), + ('\uff05', '\uff07'), ('\uff0a', '\uff0a'), + ('\uff0c', '\uff0c'), ('\uff0e', '\uff0f'), + ('\uff1a', '\uff1b'), ('\uff1f', '\uff20'), + ('\uff3c', '\uff3c'), ('\uff61', '\uff61'), + ('\uff64', '\uff65'), ('\U00010100', '\U00010102'), + ('\U0001039f', '\U0001039f'), ('\U000103d0', '\U000103d0'), + ('\U00010857', '\U00010857'), ('\U0001091f', '\U0001091f'), + ('\U0001093f', '\U0001093f'), ('\U00010a50', '\U00010a58'), + ('\U00010a7f', '\U00010a7f'), ('\U00010b39', '\U00010b3f'), + ('\U00011047', '\U0001104d'), ('\U000110bb', '\U000110bc'), + ('\U000110be', '\U000110c1'), ('\U00011140', '\U00011143'), + ('\U000111c5', '\U000111c8'), ('\U00012470', '\U00012473') + ]; + + pub fn Po(c: char) -> bool { + bsearch_range_table(c, Po_table) + } + + static Ps_table : &'static [(char,char)] = &[ + ('\x28', '\x28'), ('\x5b', '\x5b'), + ('\x7b', '\x7b'), ('\u0f3a', '\u0f3a'), + ('\u0f3c', '\u0f3c'), ('\u169b', '\u169b'), + ('\u201a', '\u201a'), ('\u201e', '\u201e'), + ('\u2045', '\u2045'), ('\u207d', '\u207d'), + ('\u208d', '\u208d'), ('\u2329', '\u2329'), + ('\u2768', '\u2768'), ('\u276a', '\u276a'), + ('\u276c', '\u276c'), ('\u276e', '\u276e'), + ('\u2770', '\u2770'), ('\u2772', '\u2772'), + ('\u2774', '\u2774'), ('\u27c5', '\u27c5'), + ('\u27e6', '\u27e6'), ('\u27e8', '\u27e8'), + ('\u27ea', '\u27ea'), ('\u27ec', '\u27ec'), + ('\u27ee', '\u27ee'), ('\u2983', '\u2983'), + ('\u2985', '\u2985'), ('\u2987', '\u2987'), + ('\u2989', '\u2989'), ('\u298b', '\u298b'), + ('\u298d', '\u298d'), ('\u298f', '\u298f'), + ('\u2991', '\u2991'), ('\u2993', '\u2993'), + ('\u2995', '\u2995'), ('\u2997', '\u2997'), + ('\u29d8', '\u29d8'), ('\u29da', '\u29da'), + ('\u29fc', '\u29fc'), ('\u2e22', '\u2e22'), + ('\u2e24', '\u2e24'), ('\u2e26', '\u2e26'), + ('\u2e28', '\u2e28'), ('\u3008', '\u3008'), + ('\u300a', '\u300a'), ('\u300c', '\u300c'), + ('\u300e', '\u300e'), ('\u3010', '\u3010'), + ('\u3014', '\u3014'), ('\u3016', '\u3016'), + ('\u3018', '\u3018'), ('\u301a', '\u301a'), + ('\u301d', '\u301d'), ('\ufd3e', '\ufd3e'), + ('\ufe17', '\ufe17'), ('\ufe35', '\ufe35'), + ('\ufe37', '\ufe37'), ('\ufe39', '\ufe39'), + ('\ufe3b', '\ufe3b'), ('\ufe3d', '\ufe3d'), + ('\ufe3f', '\ufe3f'), ('\ufe41', '\ufe41'), + ('\ufe43', '\ufe43'), ('\ufe47', '\ufe47'), + ('\ufe59', '\ufe59'), ('\ufe5b', '\ufe5b'), + ('\ufe5d', '\ufe5d'), ('\uff08', '\uff08'), + ('\uff3b', '\uff3b'), ('\uff5b', '\uff5b'), + ('\uff5f', '\uff5f'), ('\uff62', '\uff62') + ]; + + pub fn Ps(c: char) -> bool { + bsearch_range_table(c, Ps_table) + } + + static Sc_table : &'static [(char,char)] = &[ + ('\x24', '\x24'), ('\xa2', '\xa5'), + ('\u058f', '\u058f'), ('\u060b', '\u060b'), + ('\u09f2', '\u09f3'), ('\u09fb', '\u09fb'), + ('\u0af1', '\u0af1'), ('\u0bf9', '\u0bf9'), + ('\u0e3f', '\u0e3f'), ('\u17db', '\u17db'), + ('\u20a0', '\u20ba'), ('\ua838', '\ua838'), + ('\ufdfc', '\ufdfc'), ('\ufe69', '\ufe69'), + ('\uff04', '\uff04'), ('\uffe0', '\uffe1'), + ('\uffe5', '\uffe6') + ]; + + pub fn Sc(c: char) -> bool { + bsearch_range_table(c, Sc_table) + } + + static Sk_table : &'static [(char,char)] = &[ + ('\x5e', '\x5e'), ('\x60', '\x60'), + ('\xa8', '\xa8'), ('\xaf', '\xaf'), + ('\xb4', '\xb4'), ('\xb8', '\xb8'), + ('\u02c2', '\u02c5'), ('\u02d2', '\u02df'), + ('\u02e5', '\u02eb'), ('\u02ed', '\u02ed'), + ('\u02ef', '\u02ff'), ('\u0375', '\u0375'), + ('\u0384', '\u0385'), ('\u1fbd', '\u1fbd'), + ('\u1fbf', '\u1fc1'), ('\u1fcd', '\u1fcf'), + ('\u1fdd', '\u1fdf'), ('\u1fed', '\u1fef'), + ('\u1ffd', '\u1ffe'), ('\u309b', '\u309c'), + ('\ua700', '\ua716'), ('\ua720', '\ua721'), + ('\ua789', '\ua78a'), ('\ufbb2', '\ufbc1'), + ('\uff3e', '\uff3e'), ('\uff40', '\uff40'), + ('\uffe3', '\uffe3') + ]; + + pub fn Sk(c: char) -> bool { + bsearch_range_table(c, Sk_table) + } + + static Sm_table : &'static [(char,char)] = &[ + ('\x2b', '\x2b'), ('\x3c', '\x3e'), + ('\x7c', '\x7c'), ('\x7e', '\x7e'), + ('\xac', '\xac'), ('\xb1', '\xb1'), + ('\xd7', '\xd7'), ('\xf7', '\xf7'), + ('\u03f6', '\u03f6'), ('\u0606', '\u0608'), + ('\u2044', '\u2044'), ('\u2052', '\u2052'), + ('\u207a', '\u207c'), ('\u208a', '\u208c'), + ('\u2118', '\u2118'), ('\u2140', '\u2144'), + ('\u214b', '\u214b'), ('\u2190', '\u2194'), + ('\u219a', '\u219b'), ('\u21a0', '\u21a0'), + ('\u21a3', '\u21a3'), ('\u21a6', '\u21a6'), + ('\u21ae', '\u21ae'), ('\u21ce', '\u21cf'), + ('\u21d2', '\u21d2'), ('\u21d4', '\u21d4'), + ('\u21f4', '\u22ff'), ('\u2308', '\u230b'), + ('\u2320', '\u2321'), ('\u237c', '\u237c'), + ('\u239b', '\u23b3'), ('\u23dc', '\u23e1'), + ('\u25b7', '\u25b7'), ('\u25c1', '\u25c1'), + ('\u25f8', '\u25ff'), ('\u266f', '\u266f'), + ('\u27c0', '\u27c4'), ('\u27c7', '\u27e5'), + ('\u27f0', '\u27ff'), ('\u2900', '\u2982'), + ('\u2999', '\u29d7'), ('\u29dc', '\u29fb'), + ('\u29fe', '\u2aff'), ('\u2b30', '\u2b44'), + ('\u2b47', '\u2b4c'), ('\ufb29', '\ufb29'), + ('\ufe62', '\ufe62'), ('\ufe64', '\ufe66'), + ('\uff0b', '\uff0b'), ('\uff1c', '\uff1e'), + ('\uff5c', '\uff5c'), ('\uff5e', '\uff5e'), + ('\uffe2', '\uffe2'), ('\uffe9', '\uffec'), + ('\U0001d6c1', '\U0001d6c1'), ('\U0001d6db', '\U0001d6db'), + ('\U0001d6fb', '\U0001d6fb'), ('\U0001d715', '\U0001d715'), + ('\U0001d735', '\U0001d735'), ('\U0001d74f', '\U0001d74f'), + ('\U0001d76f', '\U0001d76f'), ('\U0001d789', '\U0001d789'), + ('\U0001d7a9', '\U0001d7a9'), ('\U0001d7c3', '\U0001d7c3'), + ('\U0001eef0', '\U0001eef1') + ]; + + pub fn Sm(c: char) -> bool { + bsearch_range_table(c, Sm_table) + } + + static So_table : &'static [(char,char)] = &[ + ('\xa6', '\xa6'), ('\xa9', '\xa9'), + ('\xae', '\xae'), ('\xb0', '\xb0'), + ('\u0482', '\u0482'), ('\u060e', '\u060f'), + ('\u06de', '\u06de'), ('\u06e9', '\u06e9'), + ('\u06fd', '\u06fe'), ('\u07f6', '\u07f6'), + ('\u09fa', '\u09fa'), ('\u0b70', '\u0b70'), + ('\u0bf3', '\u0bf8'), ('\u0bfa', '\u0bfa'), + ('\u0c7f', '\u0c7f'), ('\u0d79', '\u0d79'), + ('\u0f01', '\u0f03'), ('\u0f13', '\u0f13'), + ('\u0f15', '\u0f17'), ('\u0f1a', '\u0f1f'), + ('\u0f34', '\u0f34'), ('\u0f36', '\u0f36'), + ('\u0f38', '\u0f38'), ('\u0fbe', '\u0fc5'), + ('\u0fc7', '\u0fcf'), ('\u0fd5', '\u0fd8'), + ('\u109e', '\u109f'), ('\u1390', '\u1399'), + ('\u1940', '\u1940'), ('\u19de', '\u19ff'), + ('\u1b61', '\u1b6a'), ('\u1b74', '\u1b7c'), + ('\u2100', '\u2101'), ('\u2103', '\u2106'), + ('\u2108', '\u2109'), ('\u2114', '\u2114'), + ('\u2116', '\u2117'), ('\u211e', '\u2123'), + ('\u2125', '\u2125'), ('\u2127', '\u2127'), + ('\u2129', '\u2129'), ('\u212e', '\u212e'), + ('\u213a', '\u213b'), ('\u214a', '\u214a'), + ('\u214c', '\u214d'), ('\u214f', '\u214f'), + ('\u2195', '\u2199'), ('\u219c', '\u219f'), + ('\u21a1', '\u21a2'), ('\u21a4', '\u21a5'), + ('\u21a7', '\u21ad'), ('\u21af', '\u21cd'), + ('\u21d0', '\u21d1'), ('\u21d3', '\u21d3'), + ('\u21d5', '\u21f3'), ('\u2300', '\u2307'), + ('\u230c', '\u231f'), ('\u2322', '\u2328'), + ('\u232b', '\u237b'), ('\u237d', '\u239a'), + ('\u23b4', '\u23db'), ('\u23e2', '\u244a'), + ('\u249c', '\u24e9'), ('\u2500', '\u25b6'), + ('\u25b8', '\u25c0'), ('\u25c2', '\u25f7'), + ('\u2600', '\u266e'), ('\u2670', '\u2767'), + ('\u2794', '\u27bf'), ('\u2800', '\u28ff'), + ('\u2b00', '\u2b2f'), ('\u2b45', '\u2b46'), + ('\u2b50', '\u2b59'), ('\u2ce5', '\u2cea'), + ('\u2e80', '\u2ffb'), ('\u3004', '\u3004'), + ('\u3012', '\u3013'), ('\u3020', '\u3020'), + ('\u3036', '\u3037'), ('\u303e', '\u303f'), + ('\u3190', '\u3191'), ('\u3196', '\u319f'), + ('\u31c0', '\u31e3'), ('\u3200', '\u321e'), + ('\u322a', '\u3247'), ('\u3250', '\u3250'), + ('\u3260', '\u327f'), ('\u328a', '\u32b0'), + ('\u32c0', '\u33ff'), ('\u4dc0', '\u4dff'), + ('\ua490', '\ua4c6'), ('\ua828', '\ua82b'), + ('\ua836', '\ua837'), ('\ua839', '\ua839'), + ('\uaa77', '\uaa79'), ('\ufdfd', '\ufdfd'), + ('\uffe4', '\uffe4'), ('\uffe8', '\uffe8'), + ('\uffed', '\uffee'), ('\ufffc', '\ufffd'), + ('\U00010137', '\U0001013f'), ('\U00010179', '\U00010189'), + ('\U00010190', '\U000101fc'), ('\U0001d000', '\U0001d164'), + ('\U0001d16a', '\U0001d16c'), ('\U0001d183', '\U0001d184'), + ('\U0001d18c', '\U0001d1a9'), ('\U0001d1ae', '\U0001d241'), + ('\U0001d245', '\U0001d356'), ('\U0001f000', '\U0001f0df'), + ('\U0001f110', '\U0001f773') + ]; + + pub fn So(c: char) -> bool { + bsearch_range_table(c, So_table) + } + + static Zl_table : &'static [(char,char)] = &[ + ('\u2028', '\u2028') + ]; + + pub fn Zl(c: char) -> bool { + bsearch_range_table(c, Zl_table) + } + + static Zp_table : &'static [(char,char)] = &[ + ('\u2029', '\u2029') + ]; + + pub fn Zp(c: char) -> bool { + bsearch_range_table(c, Zp_table) + } + + static Zs_table : &'static [(char,char)] = &[ + ('\x20', '\x20'), ('\xa0', '\xa0'), + ('\u1680', '\u1680'), ('\u180e', '\u180e'), + ('\u2000', '\u200a'), ('\u202f', '\u202f'), + ('\u205f', '\u205f'), ('\u3000', '\u3000') + ]; + + pub fn Zs(c: char) -> bool { + bsearch_range_table(c, Zs_table) + } -Returns true if a character is an uppercase letter. -*/ -pub fn is_upper(c: char) -> bool { - return icu::libicu::u_isupper(c) == icu::TRUE; } -#[cfg(test)] -mod tests { +pub mod derived_property { - #[test] - fn test_is_digit() { - assert!((unicode::icu::is_digit('0'))); - assert!((!unicode::icu::is_digit('m'))); + + fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { + use cmp::{Equal, Less, Greater}; + use vec::bsearch; + use option::None; + (do bsearch(r) |&(lo,hi)| { + if lo <= c && c <= hi { Equal } + else if hi < c { Less } + else { Greater } + }) != None } - #[test] - fn test_is_lower() { - assert!((unicode::icu::is_lower('m'))); - assert!((!unicode::icu::is_lower('M'))); + + static Alphabetic_table : &'static [(char,char)] = &[ + ('\x41', '\x5a'), ('\x61', '\x7a'), + ('\xaa', '\xaa'), ('\xb5', '\xb5'), + ('\xba', '\xba'), ('\xc0', '\xd6'), + ('\xd8', '\xf6'), ('\xf8', '\u01ba'), + ('\u01bb', '\u01bb'), ('\u01bc', '\u01bf'), + ('\u01c0', '\u01c3'), ('\u01c4', '\u0293'), + ('\u0294', '\u0294'), ('\u0295', '\u02af'), + ('\u02b0', '\u02c1'), ('\u02c6', '\u02d1'), + ('\u02e0', '\u02e4'), ('\u02ec', '\u02ec'), + ('\u02ee', '\u02ee'), ('\u0345', '\u0345'), + ('\u0370', '\u0373'), ('\u0374', '\u0374'), + ('\u0376', '\u0377'), ('\u037a', '\u037a'), + ('\u037b', '\u037d'), ('\u0386', '\u0386'), + ('\u0388', '\u038a'), ('\u038c', '\u038c'), + ('\u038e', '\u03a1'), ('\u03a3', '\u03f5'), + ('\u03f7', '\u0481'), ('\u048a', '\u0527'), + ('\u0531', '\u0556'), ('\u0559', '\u0559'), + ('\u0561', '\u0587'), ('\u05b0', '\u05bd'), + ('\u05bf', '\u05bf'), ('\u05c1', '\u05c2'), + ('\u05c4', '\u05c5'), ('\u05c7', '\u05c7'), + ('\u05d0', '\u05ea'), ('\u05f0', '\u05f2'), + ('\u0610', '\u061a'), ('\u0620', '\u063f'), + ('\u0640', '\u0640'), ('\u0641', '\u064a'), + ('\u064b', '\u0657'), ('\u0659', '\u065f'), + ('\u066e', '\u066f'), ('\u0670', '\u0670'), + ('\u0671', '\u06d3'), ('\u06d5', '\u06d5'), + ('\u06d6', '\u06dc'), ('\u06e1', '\u06e4'), + ('\u06e5', '\u06e6'), ('\u06e7', '\u06e8'), + ('\u06ed', '\u06ed'), ('\u06ee', '\u06ef'), + ('\u06fa', '\u06fc'), ('\u06ff', '\u06ff'), + ('\u0710', '\u0710'), ('\u0711', '\u0711'), + ('\u0712', '\u072f'), ('\u0730', '\u073f'), + ('\u074d', '\u07a5'), ('\u07a6', '\u07b0'), + ('\u07b1', '\u07b1'), ('\u07ca', '\u07ea'), + ('\u07f4', '\u07f5'), ('\u07fa', '\u07fa'), + ('\u0800', '\u0815'), ('\u0816', '\u0817'), + ('\u081a', '\u081a'), ('\u081b', '\u0823'), + ('\u0824', '\u0824'), ('\u0825', '\u0827'), + ('\u0828', '\u0828'), ('\u0829', '\u082c'), + ('\u0840', '\u0858'), ('\u08a0', '\u08a0'), + ('\u08a2', '\u08ac'), ('\u08e4', '\u08e9'), + ('\u08f0', '\u08fe'), ('\u0900', '\u0902'), + ('\u0903', '\u0903'), ('\u0904', '\u0939'), + ('\u093a', '\u093a'), ('\u093b', '\u093b'), + ('\u093d', '\u093d'), ('\u093e', '\u0940'), + ('\u0941', '\u0948'), ('\u0949', '\u094c'), + ('\u094e', '\u094f'), ('\u0950', '\u0950'), + ('\u0955', '\u0957'), ('\u0958', '\u0961'), + ('\u0962', '\u0963'), ('\u0971', '\u0971'), + ('\u0972', '\u0977'), ('\u0979', '\u097f'), + ('\u0981', '\u0981'), ('\u0982', '\u0983'), + ('\u0985', '\u098c'), ('\u098f', '\u0990'), + ('\u0993', '\u09a8'), ('\u09aa', '\u09b0'), + ('\u09b2', '\u09b2'), ('\u09b6', '\u09b9'), + ('\u09bd', '\u09bd'), ('\u09be', '\u09c0'), + ('\u09c1', '\u09c4'), ('\u09c7', '\u09c8'), + ('\u09cb', '\u09cc'), ('\u09ce', '\u09ce'), + ('\u09d7', '\u09d7'), ('\u09dc', '\u09dd'), + ('\u09df', '\u09e1'), ('\u09e2', '\u09e3'), + ('\u09f0', '\u09f1'), ('\u0a01', '\u0a02'), + ('\u0a03', '\u0a03'), ('\u0a05', '\u0a0a'), + ('\u0a0f', '\u0a10'), ('\u0a13', '\u0a28'), + ('\u0a2a', '\u0a30'), ('\u0a32', '\u0a33'), + ('\u0a35', '\u0a36'), ('\u0a38', '\u0a39'), + ('\u0a3e', '\u0a40'), ('\u0a41', '\u0a42'), + ('\u0a47', '\u0a48'), ('\u0a4b', '\u0a4c'), + ('\u0a51', '\u0a51'), ('\u0a59', '\u0a5c'), + ('\u0a5e', '\u0a5e'), ('\u0a70', '\u0a71'), + ('\u0a72', '\u0a74'), ('\u0a75', '\u0a75'), + ('\u0a81', '\u0a82'), ('\u0a83', '\u0a83'), + ('\u0a85', '\u0a8d'), ('\u0a8f', '\u0a91'), + ('\u0a93', '\u0aa8'), ('\u0aaa', '\u0ab0'), + ('\u0ab2', '\u0ab3'), ('\u0ab5', '\u0ab9'), + ('\u0abd', '\u0abd'), ('\u0abe', '\u0ac0'), + ('\u0ac1', '\u0ac5'), ('\u0ac7', '\u0ac8'), + ('\u0ac9', '\u0ac9'), ('\u0acb', '\u0acc'), + ('\u0ad0', '\u0ad0'), ('\u0ae0', '\u0ae1'), + ('\u0ae2', '\u0ae3'), ('\u0b01', '\u0b01'), + ('\u0b02', '\u0b03'), ('\u0b05', '\u0b0c'), + ('\u0b0f', '\u0b10'), ('\u0b13', '\u0b28'), + ('\u0b2a', '\u0b30'), ('\u0b32', '\u0b33'), + ('\u0b35', '\u0b39'), ('\u0b3d', '\u0b3d'), + ('\u0b3e', '\u0b3e'), ('\u0b3f', '\u0b3f'), + ('\u0b40', '\u0b40'), ('\u0b41', '\u0b44'), + ('\u0b47', '\u0b48'), ('\u0b4b', '\u0b4c'), + ('\u0b56', '\u0b56'), ('\u0b57', '\u0b57'), + ('\u0b5c', '\u0b5d'), ('\u0b5f', '\u0b61'), + ('\u0b62', '\u0b63'), ('\u0b71', '\u0b71'), + ('\u0b82', '\u0b82'), ('\u0b83', '\u0b83'), + ('\u0b85', '\u0b8a'), ('\u0b8e', '\u0b90'), + ('\u0b92', '\u0b95'), ('\u0b99', '\u0b9a'), + ('\u0b9c', '\u0b9c'), ('\u0b9e', '\u0b9f'), + ('\u0ba3', '\u0ba4'), ('\u0ba8', '\u0baa'), + ('\u0bae', '\u0bb9'), ('\u0bbe', '\u0bbf'), + ('\u0bc0', '\u0bc0'), ('\u0bc1', '\u0bc2'), + ('\u0bc6', '\u0bc8'), ('\u0bca', '\u0bcc'), + ('\u0bd0', '\u0bd0'), ('\u0bd7', '\u0bd7'), + ('\u0c01', '\u0c03'), ('\u0c05', '\u0c0c'), + ('\u0c0e', '\u0c10'), ('\u0c12', '\u0c28'), + ('\u0c2a', '\u0c33'), ('\u0c35', '\u0c39'), + ('\u0c3d', '\u0c3d'), ('\u0c3e', '\u0c40'), + ('\u0c41', '\u0c44'), ('\u0c46', '\u0c48'), + ('\u0c4a', '\u0c4c'), ('\u0c55', '\u0c56'), + ('\u0c58', '\u0c59'), ('\u0c60', '\u0c61'), + ('\u0c62', '\u0c63'), ('\u0c82', '\u0c83'), + ('\u0c85', '\u0c8c'), ('\u0c8e', '\u0c90'), + ('\u0c92', '\u0ca8'), ('\u0caa', '\u0cb3'), + ('\u0cb5', '\u0cb9'), ('\u0cbd', '\u0cbd'), + ('\u0cbe', '\u0cbe'), ('\u0cbf', '\u0cbf'), + ('\u0cc0', '\u0cc4'), ('\u0cc6', '\u0cc6'), + ('\u0cc7', '\u0cc8'), ('\u0cca', '\u0ccb'), + ('\u0ccc', '\u0ccc'), ('\u0cd5', '\u0cd6'), + ('\u0cde', '\u0cde'), ('\u0ce0', '\u0ce1'), + ('\u0ce2', '\u0ce3'), ('\u0cf1', '\u0cf2'), + ('\u0d02', '\u0d03'), ('\u0d05', '\u0d0c'), + ('\u0d0e', '\u0d10'), ('\u0d12', '\u0d3a'), + ('\u0d3d', '\u0d3d'), ('\u0d3e', '\u0d40'), + ('\u0d41', '\u0d44'), ('\u0d46', '\u0d48'), + ('\u0d4a', '\u0d4c'), ('\u0d4e', '\u0d4e'), + ('\u0d57', '\u0d57'), ('\u0d60', '\u0d61'), + ('\u0d62', '\u0d63'), ('\u0d7a', '\u0d7f'), + ('\u0d82', '\u0d83'), ('\u0d85', '\u0d96'), + ('\u0d9a', '\u0db1'), ('\u0db3', '\u0dbb'), + ('\u0dbd', '\u0dbd'), ('\u0dc0', '\u0dc6'), + ('\u0dcf', '\u0dd1'), ('\u0dd2', '\u0dd4'), + ('\u0dd6', '\u0dd6'), ('\u0dd8', '\u0ddf'), + ('\u0df2', '\u0df3'), ('\u0e01', '\u0e30'), + ('\u0e31', '\u0e31'), ('\u0e32', '\u0e33'), + ('\u0e34', '\u0e3a'), ('\u0e40', '\u0e45'), + ('\u0e46', '\u0e46'), ('\u0e4d', '\u0e4d'), + ('\u0e81', '\u0e82'), ('\u0e84', '\u0e84'), + ('\u0e87', '\u0e88'), ('\u0e8a', '\u0e8a'), + ('\u0e8d', '\u0e8d'), ('\u0e94', '\u0e97'), + ('\u0e99', '\u0e9f'), ('\u0ea1', '\u0ea3'), + ('\u0ea5', '\u0ea5'), ('\u0ea7', '\u0ea7'), + ('\u0eaa', '\u0eab'), ('\u0ead', '\u0eb0'), + ('\u0eb1', '\u0eb1'), ('\u0eb2', '\u0eb3'), + ('\u0eb4', '\u0eb9'), ('\u0ebb', '\u0ebc'), + ('\u0ebd', '\u0ebd'), ('\u0ec0', '\u0ec4'), + ('\u0ec6', '\u0ec6'), ('\u0ecd', '\u0ecd'), + ('\u0edc', '\u0edf'), ('\u0f00', '\u0f00'), + ('\u0f40', '\u0f47'), ('\u0f49', '\u0f6c'), + ('\u0f71', '\u0f7e'), ('\u0f7f', '\u0f7f'), + ('\u0f80', '\u0f81'), ('\u0f88', '\u0f8c'), + ('\u0f8d', '\u0f97'), ('\u0f99', '\u0fbc'), + ('\u1000', '\u102a'), ('\u102b', '\u102c'), + ('\u102d', '\u1030'), ('\u1031', '\u1031'), + ('\u1032', '\u1036'), ('\u1038', '\u1038'), + ('\u103b', '\u103c'), ('\u103d', '\u103e'), + ('\u103f', '\u103f'), ('\u1050', '\u1055'), + ('\u1056', '\u1057'), ('\u1058', '\u1059'), + ('\u105a', '\u105d'), ('\u105e', '\u1060'), + ('\u1061', '\u1061'), ('\u1062', '\u1062'), + ('\u1065', '\u1066'), ('\u1067', '\u1068'), + ('\u106e', '\u1070'), ('\u1071', '\u1074'), + ('\u1075', '\u1081'), ('\u1082', '\u1082'), + ('\u1083', '\u1084'), ('\u1085', '\u1086'), + ('\u108e', '\u108e'), ('\u109c', '\u109c'), + ('\u109d', '\u109d'), ('\u10a0', '\u10c5'), + ('\u10c7', '\u10c7'), ('\u10cd', '\u10cd'), + ('\u10d0', '\u10fa'), ('\u10fc', '\u10fc'), + ('\u10fd', '\u1248'), ('\u124a', '\u124d'), + ('\u1250', '\u1256'), ('\u1258', '\u1258'), + ('\u125a', '\u125d'), ('\u1260', '\u1288'), + ('\u128a', '\u128d'), ('\u1290', '\u12b0'), + ('\u12b2', '\u12b5'), ('\u12b8', '\u12be'), + ('\u12c0', '\u12c0'), ('\u12c2', '\u12c5'), + ('\u12c8', '\u12d6'), ('\u12d8', '\u1310'), + ('\u1312', '\u1315'), ('\u1318', '\u135a'), + ('\u135f', '\u135f'), ('\u1380', '\u138f'), + ('\u13a0', '\u13f4'), ('\u1401', '\u166c'), + ('\u166f', '\u167f'), ('\u1681', '\u169a'), + ('\u16a0', '\u16ea'), ('\u16ee', '\u16f0'), + ('\u1700', '\u170c'), ('\u170e', '\u1711'), + ('\u1712', '\u1713'), ('\u1720', '\u1731'), + ('\u1732', '\u1733'), ('\u1740', '\u1751'), + ('\u1752', '\u1753'), ('\u1760', '\u176c'), + ('\u176e', '\u1770'), ('\u1772', '\u1773'), + ('\u1780', '\u17b3'), ('\u17b6', '\u17b6'), + ('\u17b7', '\u17bd'), ('\u17be', '\u17c5'), + ('\u17c6', '\u17c6'), ('\u17c7', '\u17c8'), + ('\u17d7', '\u17d7'), ('\u17dc', '\u17dc'), + ('\u1820', '\u1842'), ('\u1843', '\u1843'), + ('\u1844', '\u1877'), ('\u1880', '\u18a8'), + ('\u18a9', '\u18a9'), ('\u18aa', '\u18aa'), + ('\u18b0', '\u18f5'), ('\u1900', '\u191c'), + ('\u1920', '\u1922'), ('\u1923', '\u1926'), + ('\u1927', '\u1928'), ('\u1929', '\u192b'), + ('\u1930', '\u1931'), ('\u1932', '\u1932'), + ('\u1933', '\u1938'), ('\u1950', '\u196d'), + ('\u1970', '\u1974'), ('\u1980', '\u19ab'), + ('\u19b0', '\u19c0'), ('\u19c1', '\u19c7'), + ('\u19c8', '\u19c9'), ('\u1a00', '\u1a16'), + ('\u1a17', '\u1a18'), ('\u1a19', '\u1a1b'), + ('\u1a20', '\u1a54'), ('\u1a55', '\u1a55'), + ('\u1a56', '\u1a56'), ('\u1a57', '\u1a57'), + ('\u1a58', '\u1a5e'), ('\u1a61', '\u1a61'), + ('\u1a62', '\u1a62'), ('\u1a63', '\u1a64'), + ('\u1a65', '\u1a6c'), ('\u1a6d', '\u1a72'), + ('\u1a73', '\u1a74'), ('\u1aa7', '\u1aa7'), + ('\u1b00', '\u1b03'), ('\u1b04', '\u1b04'), + ('\u1b05', '\u1b33'), ('\u1b35', '\u1b35'), + ('\u1b36', '\u1b3a'), ('\u1b3b', '\u1b3b'), + ('\u1b3c', '\u1b3c'), ('\u1b3d', '\u1b41'), + ('\u1b42', '\u1b42'), ('\u1b43', '\u1b43'), + ('\u1b45', '\u1b4b'), ('\u1b80', '\u1b81'), + ('\u1b82', '\u1b82'), ('\u1b83', '\u1ba0'), + ('\u1ba1', '\u1ba1'), ('\u1ba2', '\u1ba5'), + ('\u1ba6', '\u1ba7'), ('\u1ba8', '\u1ba9'), + ('\u1bac', '\u1bad'), ('\u1bae', '\u1baf'), + ('\u1bba', '\u1be5'), ('\u1be7', '\u1be7'), + ('\u1be8', '\u1be9'), ('\u1bea', '\u1bec'), + ('\u1bed', '\u1bed'), ('\u1bee', '\u1bee'), + ('\u1bef', '\u1bf1'), ('\u1c00', '\u1c23'), + ('\u1c24', '\u1c2b'), ('\u1c2c', '\u1c33'), + ('\u1c34', '\u1c35'), ('\u1c4d', '\u1c4f'), + ('\u1c5a', '\u1c77'), ('\u1c78', '\u1c7d'), + ('\u1ce9', '\u1cec'), ('\u1cee', '\u1cf1'), + ('\u1cf2', '\u1cf3'), ('\u1cf5', '\u1cf6'), + ('\u1d00', '\u1d2b'), ('\u1d2c', '\u1d6a'), + ('\u1d6b', '\u1d77'), ('\u1d78', '\u1d78'), + ('\u1d79', '\u1d9a'), ('\u1d9b', '\u1dbf'), + ('\u1e00', '\u1f15'), ('\u1f18', '\u1f1d'), + ('\u1f20', '\u1f45'), ('\u1f48', '\u1f4d'), + ('\u1f50', '\u1f57'), ('\u1f59', '\u1f59'), + ('\u1f5b', '\u1f5b'), ('\u1f5d', '\u1f5d'), + ('\u1f5f', '\u1f7d'), ('\u1f80', '\u1fb4'), + ('\u1fb6', '\u1fbc'), ('\u1fbe', '\u1fbe'), + ('\u1fc2', '\u1fc4'), ('\u1fc6', '\u1fcc'), + ('\u1fd0', '\u1fd3'), ('\u1fd6', '\u1fdb'), + ('\u1fe0', '\u1fec'), ('\u1ff2', '\u1ff4'), + ('\u1ff6', '\u1ffc'), ('\u2071', '\u2071'), + ('\u207f', '\u207f'), ('\u2090', '\u209c'), + ('\u2102', '\u2102'), ('\u2107', '\u2107'), + ('\u210a', '\u2113'), ('\u2115', '\u2115'), + ('\u2119', '\u211d'), ('\u2124', '\u2124'), + ('\u2126', '\u2126'), ('\u2128', '\u2128'), + ('\u212a', '\u212d'), ('\u212f', '\u2134'), + ('\u2135', '\u2138'), ('\u2139', '\u2139'), + ('\u213c', '\u213f'), ('\u2145', '\u2149'), + ('\u214e', '\u214e'), ('\u2160', '\u2182'), + ('\u2183', '\u2184'), ('\u2185', '\u2188'), + ('\u24b6', '\u24e9'), ('\u2c00', '\u2c2e'), + ('\u2c30', '\u2c5e'), ('\u2c60', '\u2c7b'), + ('\u2c7c', '\u2c7d'), ('\u2c7e', '\u2ce4'), + ('\u2ceb', '\u2cee'), ('\u2cf2', '\u2cf3'), + ('\u2d00', '\u2d25'), ('\u2d27', '\u2d27'), + ('\u2d2d', '\u2d2d'), ('\u2d30', '\u2d67'), + ('\u2d6f', '\u2d6f'), ('\u2d80', '\u2d96'), + ('\u2da0', '\u2da6'), ('\u2da8', '\u2dae'), + ('\u2db0', '\u2db6'), ('\u2db8', '\u2dbe'), + ('\u2dc0', '\u2dc6'), ('\u2dc8', '\u2dce'), + ('\u2dd0', '\u2dd6'), ('\u2dd8', '\u2dde'), + ('\u2de0', '\u2dff'), ('\u2e2f', '\u2e2f'), + ('\u3005', '\u3005'), ('\u3006', '\u3006'), + ('\u3007', '\u3007'), ('\u3021', '\u3029'), + ('\u3031', '\u3035'), ('\u3038', '\u303a'), + ('\u303b', '\u303b'), ('\u303c', '\u303c'), + ('\u3041', '\u3096'), ('\u309d', '\u309e'), + ('\u309f', '\u309f'), ('\u30a1', '\u30fa'), + ('\u30fc', '\u30fe'), ('\u30ff', '\u30ff'), + ('\u3105', '\u312d'), ('\u3131', '\u318e'), + ('\u31a0', '\u31ba'), ('\u31f0', '\u31ff'), + ('\u3400', '\u4db5'), ('\u4e00', '\u9fcc'), + ('\ua000', '\ua014'), ('\ua015', '\ua015'), + ('\ua016', '\ua48c'), ('\ua4d0', '\ua4f7'), + ('\ua4f8', '\ua4fd'), ('\ua500', '\ua60b'), + ('\ua60c', '\ua60c'), ('\ua610', '\ua61f'), + ('\ua62a', '\ua62b'), ('\ua640', '\ua66d'), + ('\ua66e', '\ua66e'), ('\ua674', '\ua67b'), + ('\ua67f', '\ua67f'), ('\ua680', '\ua697'), + ('\ua69f', '\ua69f'), ('\ua6a0', '\ua6e5'), + ('\ua6e6', '\ua6ef'), ('\ua717', '\ua71f'), + ('\ua722', '\ua76f'), ('\ua770', '\ua770'), + ('\ua771', '\ua787'), ('\ua788', '\ua788'), + ('\ua78b', '\ua78e'), ('\ua790', '\ua793'), + ('\ua7a0', '\ua7aa'), ('\ua7f8', '\ua7f9'), + ('\ua7fa', '\ua7fa'), ('\ua7fb', '\ua801'), + ('\ua803', '\ua805'), ('\ua807', '\ua80a'), + ('\ua80c', '\ua822'), ('\ua823', '\ua824'), + ('\ua825', '\ua826'), ('\ua827', '\ua827'), + ('\ua840', '\ua873'), ('\ua880', '\ua881'), + ('\ua882', '\ua8b3'), ('\ua8b4', '\ua8c3'), + ('\ua8f2', '\ua8f7'), ('\ua8fb', '\ua8fb'), + ('\ua90a', '\ua925'), ('\ua926', '\ua92a'), + ('\ua930', '\ua946'), ('\ua947', '\ua951'), + ('\ua952', '\ua952'), ('\ua960', '\ua97c'), + ('\ua980', '\ua982'), ('\ua983', '\ua983'), + ('\ua984', '\ua9b2'), ('\ua9b4', '\ua9b5'), + ('\ua9b6', '\ua9b9'), ('\ua9ba', '\ua9bb'), + ('\ua9bc', '\ua9bc'), ('\ua9bd', '\ua9bf'), + ('\ua9cf', '\ua9cf'), ('\uaa00', '\uaa28'), + ('\uaa29', '\uaa2e'), ('\uaa2f', '\uaa30'), + ('\uaa31', '\uaa32'), ('\uaa33', '\uaa34'), + ('\uaa35', '\uaa36'), ('\uaa40', '\uaa42'), + ('\uaa43', '\uaa43'), ('\uaa44', '\uaa4b'), + ('\uaa4c', '\uaa4c'), ('\uaa4d', '\uaa4d'), + ('\uaa60', '\uaa6f'), ('\uaa70', '\uaa70'), + ('\uaa71', '\uaa76'), ('\uaa7a', '\uaa7a'), + ('\uaa80', '\uaaaf'), ('\uaab0', '\uaab0'), + ('\uaab1', '\uaab1'), ('\uaab2', '\uaab4'), + ('\uaab5', '\uaab6'), ('\uaab7', '\uaab8'), + ('\uaab9', '\uaabd'), ('\uaabe', '\uaabe'), + ('\uaac0', '\uaac0'), ('\uaac2', '\uaac2'), + ('\uaadb', '\uaadc'), ('\uaadd', '\uaadd'), + ('\uaae0', '\uaaea'), ('\uaaeb', '\uaaeb'), + ('\uaaec', '\uaaed'), ('\uaaee', '\uaaef'), + ('\uaaf2', '\uaaf2'), ('\uaaf3', '\uaaf4'), + ('\uaaf5', '\uaaf5'), ('\uab01', '\uab06'), + ('\uab09', '\uab0e'), ('\uab11', '\uab16'), + ('\uab20', '\uab26'), ('\uab28', '\uab2e'), + ('\uabc0', '\uabe2'), ('\uabe3', '\uabe4'), + ('\uabe5', '\uabe5'), ('\uabe6', '\uabe7'), + ('\uabe8', '\uabe8'), ('\uabe9', '\uabea'), + ('\uac00', '\ud7a3'), ('\ud7b0', '\ud7c6'), + ('\ud7cb', '\ud7fb'), ('\uf900', '\ufa6d'), + ('\ufa70', '\ufad9'), ('\ufb00', '\ufb06'), + ('\ufb13', '\ufb17'), ('\ufb1d', '\ufb1d'), + ('\ufb1e', '\ufb1e'), ('\ufb1f', '\ufb28'), + ('\ufb2a', '\ufb36'), ('\ufb38', '\ufb3c'), + ('\ufb3e', '\ufb3e'), ('\ufb40', '\ufb41'), + ('\ufb43', '\ufb44'), ('\ufb46', '\ufbb1'), + ('\ufbd3', '\ufd3d'), ('\ufd50', '\ufd8f'), + ('\ufd92', '\ufdc7'), ('\ufdf0', '\ufdfb'), + ('\ufe70', '\ufe74'), ('\ufe76', '\ufefc'), + ('\uff21', '\uff3a'), ('\uff41', '\uff5a'), + ('\uff66', '\uff6f'), ('\uff70', '\uff70'), + ('\uff71', '\uff9d'), ('\uff9e', '\uff9f'), + ('\uffa0', '\uffbe'), ('\uffc2', '\uffc7'), + ('\uffca', '\uffcf'), ('\uffd2', '\uffd7'), + ('\uffda', '\uffdc'), ('\U00010000', '\U0001000b'), + ('\U0001000d', '\U00010026'), ('\U00010028', '\U0001003a'), + ('\U0001003c', '\U0001003d'), ('\U0001003f', '\U0001004d'), + ('\U00010050', '\U0001005d'), ('\U00010080', '\U000100fa'), + ('\U00010140', '\U00010174'), ('\U00010280', '\U0001029c'), + ('\U000102a0', '\U000102d0'), ('\U00010300', '\U0001031e'), + ('\U00010330', '\U00010340'), ('\U00010341', '\U00010341'), + ('\U00010342', '\U00010349'), ('\U0001034a', '\U0001034a'), + ('\U00010380', '\U0001039d'), ('\U000103a0', '\U000103c3'), + ('\U000103c8', '\U000103cf'), ('\U000103d1', '\U000103d5'), + ('\U00010400', '\U0001044f'), ('\U00010450', '\U0001049d'), + ('\U00010800', '\U00010805'), ('\U00010808', '\U00010808'), + ('\U0001080a', '\U00010835'), ('\U00010837', '\U00010838'), + ('\U0001083c', '\U0001083c'), ('\U0001083f', '\U00010855'), + ('\U00010900', '\U00010915'), ('\U00010920', '\U00010939'), + ('\U00010980', '\U000109b7'), ('\U000109be', '\U000109bf'), + ('\U00010a00', '\U00010a00'), ('\U00010a01', '\U00010a03'), + ('\U00010a05', '\U00010a06'), ('\U00010a0c', '\U00010a0f'), + ('\U00010a10', '\U00010a13'), ('\U00010a15', '\U00010a17'), + ('\U00010a19', '\U00010a33'), ('\U00010a60', '\U00010a7c'), + ('\U00010b00', '\U00010b35'), ('\U00010b40', '\U00010b55'), + ('\U00010b60', '\U00010b72'), ('\U00010c00', '\U00010c48'), + ('\U00011000', '\U00011000'), ('\U00011001', '\U00011001'), + ('\U00011002', '\U00011002'), ('\U00011003', '\U00011037'), + ('\U00011038', '\U00011045'), ('\U00011082', '\U00011082'), + ('\U00011083', '\U000110af'), ('\U000110b0', '\U000110b2'), + ('\U000110b3', '\U000110b6'), ('\U000110b7', '\U000110b8'), + ('\U000110d0', '\U000110e8'), ('\U00011100', '\U00011102'), + ('\U00011103', '\U00011126'), ('\U00011127', '\U0001112b'), + ('\U0001112c', '\U0001112c'), ('\U0001112d', '\U00011132'), + ('\U00011180', '\U00011181'), ('\U00011182', '\U00011182'), + ('\U00011183', '\U000111b2'), ('\U000111b3', '\U000111b5'), + ('\U000111b6', '\U000111be'), ('\U000111bf', '\U000111bf'), + ('\U000111c1', '\U000111c4'), ('\U00011680', '\U000116aa'), + ('\U000116ab', '\U000116ab'), ('\U000116ac', '\U000116ac'), + ('\U000116ad', '\U000116ad'), ('\U000116ae', '\U000116af'), + ('\U000116b0', '\U000116b5'), ('\U00012000', '\U0001236e'), + ('\U00012400', '\U00012462'), ('\U00013000', '\U0001342e'), + ('\U00016800', '\U00016a38'), ('\U00016f00', '\U00016f44'), + ('\U00016f50', '\U00016f50'), ('\U00016f51', '\U00016f7e'), + ('\U00016f93', '\U00016f9f'), ('\U0001b000', '\U0001b001'), + ('\U0001d400', '\U0001d454'), ('\U0001d456', '\U0001d49c'), + ('\U0001d49e', '\U0001d49f'), ('\U0001d4a2', '\U0001d4a2'), + ('\U0001d4a5', '\U0001d4a6'), ('\U0001d4a9', '\U0001d4ac'), + ('\U0001d4ae', '\U0001d4b9'), ('\U0001d4bb', '\U0001d4bb'), + ('\U0001d4bd', '\U0001d4c3'), ('\U0001d4c5', '\U0001d505'), + ('\U0001d507', '\U0001d50a'), ('\U0001d50d', '\U0001d514'), + ('\U0001d516', '\U0001d51c'), ('\U0001d51e', '\U0001d539'), + ('\U0001d53b', '\U0001d53e'), ('\U0001d540', '\U0001d544'), + ('\U0001d546', '\U0001d546'), ('\U0001d54a', '\U0001d550'), + ('\U0001d552', '\U0001d6a5'), ('\U0001d6a8', '\U0001d6c0'), + ('\U0001d6c2', '\U0001d6da'), ('\U0001d6dc', '\U0001d6fa'), + ('\U0001d6fc', '\U0001d714'), ('\U0001d716', '\U0001d734'), + ('\U0001d736', '\U0001d74e'), ('\U0001d750', '\U0001d76e'), + ('\U0001d770', '\U0001d788'), ('\U0001d78a', '\U0001d7a8'), + ('\U0001d7aa', '\U0001d7c2'), ('\U0001d7c4', '\U0001d7cb'), + ('\U0001ee00', '\U0001ee03'), ('\U0001ee05', '\U0001ee1f'), + ('\U0001ee21', '\U0001ee22'), ('\U0001ee24', '\U0001ee24'), + ('\U0001ee27', '\U0001ee27'), ('\U0001ee29', '\U0001ee32'), + ('\U0001ee34', '\U0001ee37'), ('\U0001ee39', '\U0001ee39'), + ('\U0001ee3b', '\U0001ee3b'), ('\U0001ee42', '\U0001ee42'), + ('\U0001ee47', '\U0001ee47'), ('\U0001ee49', '\U0001ee49'), + ('\U0001ee4b', '\U0001ee4b'), ('\U0001ee4d', '\U0001ee4f'), + ('\U0001ee51', '\U0001ee52'), ('\U0001ee54', '\U0001ee54'), + ('\U0001ee57', '\U0001ee57'), ('\U0001ee59', '\U0001ee59'), + ('\U0001ee5b', '\U0001ee5b'), ('\U0001ee5d', '\U0001ee5d'), + ('\U0001ee5f', '\U0001ee5f'), ('\U0001ee61', '\U0001ee62'), + ('\U0001ee64', '\U0001ee64'), ('\U0001ee67', '\U0001ee6a'), + ('\U0001ee6c', '\U0001ee72'), ('\U0001ee74', '\U0001ee77'), + ('\U0001ee79', '\U0001ee7c'), ('\U0001ee7e', '\U0001ee7e'), + ('\U0001ee80', '\U0001ee89'), ('\U0001ee8b', '\U0001ee9b'), + ('\U0001eea1', '\U0001eea3'), ('\U0001eea5', '\U0001eea9'), + ('\U0001eeab', '\U0001eebb'), ('\U00020000', '\U0002a6d6'), + ('\U0002a700', '\U0002b734'), ('\U0002b740', '\U0002b81d'), + ('\U0002f800', '\U0002fa1d') + ]; + + pub fn Alphabetic(c: char) -> bool { + bsearch_range_table(c, Alphabetic_table) } - #[test] - fn test_is_space() { - assert!((unicode::icu::is_space(' '))); - assert!((!unicode::icu::is_space('m'))); + static XID_Continue_table : &'static [(char,char)] = &[ + ('\x30', '\x39'), ('\x41', '\x5a'), + ('\x5f', '\x5f'), ('\x61', '\x7a'), + ('\xaa', '\xaa'), ('\xb5', '\xb5'), + ('\xb7', '\xb7'), ('\xba', '\xba'), + ('\xc0', '\xd6'), ('\xd8', '\xf6'), + ('\xf8', '\u01ba'), ('\u01bb', '\u01bb'), + ('\u01bc', '\u01bf'), ('\u01c0', '\u01c3'), + ('\u01c4', '\u0293'), ('\u0294', '\u0294'), + ('\u0295', '\u02af'), ('\u02b0', '\u02c1'), + ('\u02c6', '\u02d1'), ('\u02e0', '\u02e4'), + ('\u02ec', '\u02ec'), ('\u02ee', '\u02ee'), + ('\u0300', '\u036f'), ('\u0370', '\u0373'), + ('\u0374', '\u0374'), ('\u0376', '\u0377'), + ('\u037b', '\u037d'), ('\u0386', '\u0386'), + ('\u0387', '\u0387'), ('\u0388', '\u038a'), + ('\u038c', '\u038c'), ('\u038e', '\u03a1'), + ('\u03a3', '\u03f5'), ('\u03f7', '\u0481'), + ('\u0483', '\u0487'), ('\u048a', '\u0527'), + ('\u0531', '\u0556'), ('\u0559', '\u0559'), + ('\u0561', '\u0587'), ('\u0591', '\u05bd'), + ('\u05bf', '\u05bf'), ('\u05c1', '\u05c2'), + ('\u05c4', '\u05c5'), ('\u05c7', '\u05c7'), + ('\u05d0', '\u05ea'), ('\u05f0', '\u05f2'), + ('\u0610', '\u061a'), ('\u0620', '\u063f'), + ('\u0640', '\u0640'), ('\u0641', '\u064a'), + ('\u064b', '\u065f'), ('\u0660', '\u0669'), + ('\u066e', '\u066f'), ('\u0670', '\u0670'), + ('\u0671', '\u06d3'), ('\u06d5', '\u06d5'), + ('\u06d6', '\u06dc'), ('\u06df', '\u06e4'), + ('\u06e5', '\u06e6'), ('\u06e7', '\u06e8'), + ('\u06ea', '\u06ed'), ('\u06ee', '\u06ef'), + ('\u06f0', '\u06f9'), ('\u06fa', '\u06fc'), + ('\u06ff', '\u06ff'), ('\u0710', '\u0710'), + ('\u0711', '\u0711'), ('\u0712', '\u072f'), + ('\u0730', '\u074a'), ('\u074d', '\u07a5'), + ('\u07a6', '\u07b0'), ('\u07b1', '\u07b1'), + ('\u07c0', '\u07c9'), ('\u07ca', '\u07ea'), + ('\u07eb', '\u07f3'), ('\u07f4', '\u07f5'), + ('\u07fa', '\u07fa'), ('\u0800', '\u0815'), + ('\u0816', '\u0819'), ('\u081a', '\u081a'), + ('\u081b', '\u0823'), ('\u0824', '\u0824'), + ('\u0825', '\u0827'), ('\u0828', '\u0828'), + ('\u0829', '\u082d'), ('\u0840', '\u0858'), + ('\u0859', '\u085b'), ('\u08a0', '\u08a0'), + ('\u08a2', '\u08ac'), ('\u08e4', '\u08fe'), + ('\u0900', '\u0902'), ('\u0903', '\u0903'), + ('\u0904', '\u0939'), ('\u093a', '\u093a'), + ('\u093b', '\u093b'), ('\u093c', '\u093c'), + ('\u093d', '\u093d'), ('\u093e', '\u0940'), + ('\u0941', '\u0948'), ('\u0949', '\u094c'), + ('\u094d', '\u094d'), ('\u094e', '\u094f'), + ('\u0950', '\u0950'), ('\u0951', '\u0957'), + ('\u0958', '\u0961'), ('\u0962', '\u0963'), + ('\u0966', '\u096f'), ('\u0971', '\u0971'), + ('\u0972', '\u0977'), ('\u0979', '\u097f'), + ('\u0981', '\u0981'), ('\u0982', '\u0983'), + ('\u0985', '\u098c'), ('\u098f', '\u0990'), + ('\u0993', '\u09a8'), ('\u09aa', '\u09b0'), + ('\u09b2', '\u09b2'), ('\u09b6', '\u09b9'), + ('\u09bc', '\u09bc'), ('\u09bd', '\u09bd'), + ('\u09be', '\u09c0'), ('\u09c1', '\u09c4'), + ('\u09c7', '\u09c8'), ('\u09cb', '\u09cc'), + ('\u09cd', '\u09cd'), ('\u09ce', '\u09ce'), + ('\u09d7', '\u09d7'), ('\u09dc', '\u09dd'), + ('\u09df', '\u09e1'), ('\u09e2', '\u09e3'), + ('\u09e6', '\u09ef'), ('\u09f0', '\u09f1'), + ('\u0a01', '\u0a02'), ('\u0a03', '\u0a03'), + ('\u0a05', '\u0a0a'), ('\u0a0f', '\u0a10'), + ('\u0a13', '\u0a28'), ('\u0a2a', '\u0a30'), + ('\u0a32', '\u0a33'), ('\u0a35', '\u0a36'), + ('\u0a38', '\u0a39'), ('\u0a3c', '\u0a3c'), + ('\u0a3e', '\u0a40'), ('\u0a41', '\u0a42'), + ('\u0a47', '\u0a48'), ('\u0a4b', '\u0a4d'), + ('\u0a51', '\u0a51'), ('\u0a59', '\u0a5c'), + ('\u0a5e', '\u0a5e'), ('\u0a66', '\u0a6f'), + ('\u0a70', '\u0a71'), ('\u0a72', '\u0a74'), + ('\u0a75', '\u0a75'), ('\u0a81', '\u0a82'), + ('\u0a83', '\u0a83'), ('\u0a85', '\u0a8d'), + ('\u0a8f', '\u0a91'), ('\u0a93', '\u0aa8'), + ('\u0aaa', '\u0ab0'), ('\u0ab2', '\u0ab3'), + ('\u0ab5', '\u0ab9'), ('\u0abc', '\u0abc'), + ('\u0abd', '\u0abd'), ('\u0abe', '\u0ac0'), + ('\u0ac1', '\u0ac5'), ('\u0ac7', '\u0ac8'), + ('\u0ac9', '\u0ac9'), ('\u0acb', '\u0acc'), + ('\u0acd', '\u0acd'), ('\u0ad0', '\u0ad0'), + ('\u0ae0', '\u0ae1'), ('\u0ae2', '\u0ae3'), + ('\u0ae6', '\u0aef'), ('\u0b01', '\u0b01'), + ('\u0b02', '\u0b03'), ('\u0b05', '\u0b0c'), + ('\u0b0f', '\u0b10'), ('\u0b13', '\u0b28'), + ('\u0b2a', '\u0b30'), ('\u0b32', '\u0b33'), + ('\u0b35', '\u0b39'), ('\u0b3c', '\u0b3c'), + ('\u0b3d', '\u0b3d'), ('\u0b3e', '\u0b3e'), + ('\u0b3f', '\u0b3f'), ('\u0b40', '\u0b40'), + ('\u0b41', '\u0b44'), ('\u0b47', '\u0b48'), + ('\u0b4b', '\u0b4c'), ('\u0b4d', '\u0b4d'), + ('\u0b56', '\u0b56'), ('\u0b57', '\u0b57'), + ('\u0b5c', '\u0b5d'), ('\u0b5f', '\u0b61'), + ('\u0b62', '\u0b63'), ('\u0b66', '\u0b6f'), + ('\u0b71', '\u0b71'), ('\u0b82', '\u0b82'), + ('\u0b83', '\u0b83'), ('\u0b85', '\u0b8a'), + ('\u0b8e', '\u0b90'), ('\u0b92', '\u0b95'), + ('\u0b99', '\u0b9a'), ('\u0b9c', '\u0b9c'), + ('\u0b9e', '\u0b9f'), ('\u0ba3', '\u0ba4'), + ('\u0ba8', '\u0baa'), ('\u0bae', '\u0bb9'), + ('\u0bbe', '\u0bbf'), ('\u0bc0', '\u0bc0'), + ('\u0bc1', '\u0bc2'), ('\u0bc6', '\u0bc8'), + ('\u0bca', '\u0bcc'), ('\u0bcd', '\u0bcd'), + ('\u0bd0', '\u0bd0'), ('\u0bd7', '\u0bd7'), + ('\u0be6', '\u0bef'), ('\u0c01', '\u0c03'), + ('\u0c05', '\u0c0c'), ('\u0c0e', '\u0c10'), + ('\u0c12', '\u0c28'), ('\u0c2a', '\u0c33'), + ('\u0c35', '\u0c39'), ('\u0c3d', '\u0c3d'), + ('\u0c3e', '\u0c40'), ('\u0c41', '\u0c44'), + ('\u0c46', '\u0c48'), ('\u0c4a', '\u0c4d'), + ('\u0c55', '\u0c56'), ('\u0c58', '\u0c59'), + ('\u0c60', '\u0c61'), ('\u0c62', '\u0c63'), + ('\u0c66', '\u0c6f'), ('\u0c82', '\u0c83'), + ('\u0c85', '\u0c8c'), ('\u0c8e', '\u0c90'), + ('\u0c92', '\u0ca8'), ('\u0caa', '\u0cb3'), + ('\u0cb5', '\u0cb9'), ('\u0cbc', '\u0cbc'), + ('\u0cbd', '\u0cbd'), ('\u0cbe', '\u0cbe'), + ('\u0cbf', '\u0cbf'), ('\u0cc0', '\u0cc4'), + ('\u0cc6', '\u0cc6'), ('\u0cc7', '\u0cc8'), + ('\u0cca', '\u0ccb'), ('\u0ccc', '\u0ccd'), + ('\u0cd5', '\u0cd6'), ('\u0cde', '\u0cde'), + ('\u0ce0', '\u0ce1'), ('\u0ce2', '\u0ce3'), + ('\u0ce6', '\u0cef'), ('\u0cf1', '\u0cf2'), + ('\u0d02', '\u0d03'), ('\u0d05', '\u0d0c'), + ('\u0d0e', '\u0d10'), ('\u0d12', '\u0d3a'), + ('\u0d3d', '\u0d3d'), ('\u0d3e', '\u0d40'), + ('\u0d41', '\u0d44'), ('\u0d46', '\u0d48'), + ('\u0d4a', '\u0d4c'), ('\u0d4d', '\u0d4d'), + ('\u0d4e', '\u0d4e'), ('\u0d57', '\u0d57'), + ('\u0d60', '\u0d61'), ('\u0d62', '\u0d63'), + ('\u0d66', '\u0d6f'), ('\u0d7a', '\u0d7f'), + ('\u0d82', '\u0d83'), ('\u0d85', '\u0d96'), + ('\u0d9a', '\u0db1'), ('\u0db3', '\u0dbb'), + ('\u0dbd', '\u0dbd'), ('\u0dc0', '\u0dc6'), + ('\u0dca', '\u0dca'), ('\u0dcf', '\u0dd1'), + ('\u0dd2', '\u0dd4'), ('\u0dd6', '\u0dd6'), + ('\u0dd8', '\u0ddf'), ('\u0df2', '\u0df3'), + ('\u0e01', '\u0e30'), ('\u0e31', '\u0e31'), + ('\u0e32', '\u0e33'), ('\u0e34', '\u0e3a'), + ('\u0e40', '\u0e45'), ('\u0e46', '\u0e46'), + ('\u0e47', '\u0e4e'), ('\u0e50', '\u0e59'), + ('\u0e81', '\u0e82'), ('\u0e84', '\u0e84'), + ('\u0e87', '\u0e88'), ('\u0e8a', '\u0e8a'), + ('\u0e8d', '\u0e8d'), ('\u0e94', '\u0e97'), + ('\u0e99', '\u0e9f'), ('\u0ea1', '\u0ea3'), + ('\u0ea5', '\u0ea5'), ('\u0ea7', '\u0ea7'), + ('\u0eaa', '\u0eab'), ('\u0ead', '\u0eb0'), + ('\u0eb1', '\u0eb1'), ('\u0eb2', '\u0eb3'), + ('\u0eb4', '\u0eb9'), ('\u0ebb', '\u0ebc'), + ('\u0ebd', '\u0ebd'), ('\u0ec0', '\u0ec4'), + ('\u0ec6', '\u0ec6'), ('\u0ec8', '\u0ecd'), + ('\u0ed0', '\u0ed9'), ('\u0edc', '\u0edf'), + ('\u0f00', '\u0f00'), ('\u0f18', '\u0f19'), + ('\u0f20', '\u0f29'), ('\u0f35', '\u0f35'), + ('\u0f37', '\u0f37'), ('\u0f39', '\u0f39'), + ('\u0f3e', '\u0f3f'), ('\u0f40', '\u0f47'), + ('\u0f49', '\u0f6c'), ('\u0f71', '\u0f7e'), + ('\u0f7f', '\u0f7f'), ('\u0f80', '\u0f84'), + ('\u0f86', '\u0f87'), ('\u0f88', '\u0f8c'), + ('\u0f8d', '\u0f97'), ('\u0f99', '\u0fbc'), + ('\u0fc6', '\u0fc6'), ('\u1000', '\u102a'), + ('\u102b', '\u102c'), ('\u102d', '\u1030'), + ('\u1031', '\u1031'), ('\u1032', '\u1037'), + ('\u1038', '\u1038'), ('\u1039', '\u103a'), + ('\u103b', '\u103c'), ('\u103d', '\u103e'), + ('\u103f', '\u103f'), ('\u1040', '\u1049'), + ('\u1050', '\u1055'), ('\u1056', '\u1057'), + ('\u1058', '\u1059'), ('\u105a', '\u105d'), + ('\u105e', '\u1060'), ('\u1061', '\u1061'), + ('\u1062', '\u1064'), ('\u1065', '\u1066'), + ('\u1067', '\u106d'), ('\u106e', '\u1070'), + ('\u1071', '\u1074'), ('\u1075', '\u1081'), + ('\u1082', '\u1082'), ('\u1083', '\u1084'), + ('\u1085', '\u1086'), ('\u1087', '\u108c'), + ('\u108d', '\u108d'), ('\u108e', '\u108e'), + ('\u108f', '\u108f'), ('\u1090', '\u1099'), + ('\u109a', '\u109c'), ('\u109d', '\u109d'), + ('\u10a0', '\u10c5'), ('\u10c7', '\u10c7'), + ('\u10cd', '\u10cd'), ('\u10d0', '\u10fa'), + ('\u10fc', '\u10fc'), ('\u10fd', '\u1248'), + ('\u124a', '\u124d'), ('\u1250', '\u1256'), + ('\u1258', '\u1258'), ('\u125a', '\u125d'), + ('\u1260', '\u1288'), ('\u128a', '\u128d'), + ('\u1290', '\u12b0'), ('\u12b2', '\u12b5'), + ('\u12b8', '\u12be'), ('\u12c0', '\u12c0'), + ('\u12c2', '\u12c5'), ('\u12c8', '\u12d6'), + ('\u12d8', '\u1310'), ('\u1312', '\u1315'), + ('\u1318', '\u135a'), ('\u135d', '\u135f'), + ('\u1369', '\u1371'), ('\u1380', '\u138f'), + ('\u13a0', '\u13f4'), ('\u1401', '\u166c'), + ('\u166f', '\u167f'), ('\u1681', '\u169a'), + ('\u16a0', '\u16ea'), ('\u16ee', '\u16f0'), + ('\u1700', '\u170c'), ('\u170e', '\u1711'), + ('\u1712', '\u1714'), ('\u1720', '\u1731'), + ('\u1732', '\u1734'), ('\u1740', '\u1751'), + ('\u1752', '\u1753'), ('\u1760', '\u176c'), + ('\u176e', '\u1770'), ('\u1772', '\u1773'), + ('\u1780', '\u17b3'), ('\u17b4', '\u17b5'), + ('\u17b6', '\u17b6'), ('\u17b7', '\u17bd'), + ('\u17be', '\u17c5'), ('\u17c6', '\u17c6'), + ('\u17c7', '\u17c8'), ('\u17c9', '\u17d3'), + ('\u17d7', '\u17d7'), ('\u17dc', '\u17dc'), + ('\u17dd', '\u17dd'), ('\u17e0', '\u17e9'), + ('\u180b', '\u180d'), ('\u1810', '\u1819'), + ('\u1820', '\u1842'), ('\u1843', '\u1843'), + ('\u1844', '\u1877'), ('\u1880', '\u18a8'), + ('\u18a9', '\u18a9'), ('\u18aa', '\u18aa'), + ('\u18b0', '\u18f5'), ('\u1900', '\u191c'), + ('\u1920', '\u1922'), ('\u1923', '\u1926'), + ('\u1927', '\u1928'), ('\u1929', '\u192b'), + ('\u1930', '\u1931'), ('\u1932', '\u1932'), + ('\u1933', '\u1938'), ('\u1939', '\u193b'), + ('\u1946', '\u194f'), ('\u1950', '\u196d'), + ('\u1970', '\u1974'), ('\u1980', '\u19ab'), + ('\u19b0', '\u19c0'), ('\u19c1', '\u19c7'), + ('\u19c8', '\u19c9'), ('\u19d0', '\u19d9'), + ('\u19da', '\u19da'), ('\u1a00', '\u1a16'), + ('\u1a17', '\u1a18'), ('\u1a19', '\u1a1b'), + ('\u1a20', '\u1a54'), ('\u1a55', '\u1a55'), + ('\u1a56', '\u1a56'), ('\u1a57', '\u1a57'), + ('\u1a58', '\u1a5e'), ('\u1a60', '\u1a60'), + ('\u1a61', '\u1a61'), ('\u1a62', '\u1a62'), + ('\u1a63', '\u1a64'), ('\u1a65', '\u1a6c'), + ('\u1a6d', '\u1a72'), ('\u1a73', '\u1a7c'), + ('\u1a7f', '\u1a7f'), ('\u1a80', '\u1a89'), + ('\u1a90', '\u1a99'), ('\u1aa7', '\u1aa7'), + ('\u1b00', '\u1b03'), ('\u1b04', '\u1b04'), + ('\u1b05', '\u1b33'), ('\u1b34', '\u1b34'), + ('\u1b35', '\u1b35'), ('\u1b36', '\u1b3a'), + ('\u1b3b', '\u1b3b'), ('\u1b3c', '\u1b3c'), + ('\u1b3d', '\u1b41'), ('\u1b42', '\u1b42'), + ('\u1b43', '\u1b44'), ('\u1b45', '\u1b4b'), + ('\u1b50', '\u1b59'), ('\u1b6b', '\u1b73'), + ('\u1b80', '\u1b81'), ('\u1b82', '\u1b82'), + ('\u1b83', '\u1ba0'), ('\u1ba1', '\u1ba1'), + ('\u1ba2', '\u1ba5'), ('\u1ba6', '\u1ba7'), + ('\u1ba8', '\u1ba9'), ('\u1baa', '\u1baa'), + ('\u1bab', '\u1bab'), ('\u1bac', '\u1bad'), + ('\u1bae', '\u1baf'), ('\u1bb0', '\u1bb9'), + ('\u1bba', '\u1be5'), ('\u1be6', '\u1be6'), + ('\u1be7', '\u1be7'), ('\u1be8', '\u1be9'), + ('\u1bea', '\u1bec'), ('\u1bed', '\u1bed'), + ('\u1bee', '\u1bee'), ('\u1bef', '\u1bf1'), + ('\u1bf2', '\u1bf3'), ('\u1c00', '\u1c23'), + ('\u1c24', '\u1c2b'), ('\u1c2c', '\u1c33'), + ('\u1c34', '\u1c35'), ('\u1c36', '\u1c37'), + ('\u1c40', '\u1c49'), ('\u1c4d', '\u1c4f'), + ('\u1c50', '\u1c59'), ('\u1c5a', '\u1c77'), + ('\u1c78', '\u1c7d'), ('\u1cd0', '\u1cd2'), + ('\u1cd4', '\u1ce0'), ('\u1ce1', '\u1ce1'), + ('\u1ce2', '\u1ce8'), ('\u1ce9', '\u1cec'), + ('\u1ced', '\u1ced'), ('\u1cee', '\u1cf1'), + ('\u1cf2', '\u1cf3'), ('\u1cf4', '\u1cf4'), + ('\u1cf5', '\u1cf6'), ('\u1d00', '\u1d2b'), + ('\u1d2c', '\u1d6a'), ('\u1d6b', '\u1d77'), + ('\u1d78', '\u1d78'), ('\u1d79', '\u1d9a'), + ('\u1d9b', '\u1dbf'), ('\u1dc0', '\u1de6'), + ('\u1dfc', '\u1dff'), ('\u1e00', '\u1f15'), + ('\u1f18', '\u1f1d'), ('\u1f20', '\u1f45'), + ('\u1f48', '\u1f4d'), ('\u1f50', '\u1f57'), + ('\u1f59', '\u1f59'), ('\u1f5b', '\u1f5b'), + ('\u1f5d', '\u1f5d'), ('\u1f5f', '\u1f7d'), + ('\u1f80', '\u1fb4'), ('\u1fb6', '\u1fbc'), + ('\u1fbe', '\u1fbe'), ('\u1fc2', '\u1fc4'), + ('\u1fc6', '\u1fcc'), ('\u1fd0', '\u1fd3'), + ('\u1fd6', '\u1fdb'), ('\u1fe0', '\u1fec'), + ('\u1ff2', '\u1ff4'), ('\u1ff6', '\u1ffc'), + ('\u203f', '\u2040'), ('\u2054', '\u2054'), + ('\u2071', '\u2071'), ('\u207f', '\u207f'), + ('\u2090', '\u209c'), ('\u20d0', '\u20dc'), + ('\u20e1', '\u20e1'), ('\u20e5', '\u20f0'), + ('\u2102', '\u2102'), ('\u2107', '\u2107'), + ('\u210a', '\u2113'), ('\u2115', '\u2115'), + ('\u2118', '\u2118'), ('\u2119', '\u211d'), + ('\u2124', '\u2124'), ('\u2126', '\u2126'), + ('\u2128', '\u2128'), ('\u212a', '\u212d'), + ('\u212e', '\u212e'), ('\u212f', '\u2134'), + ('\u2135', '\u2138'), ('\u2139', '\u2139'), + ('\u213c', '\u213f'), ('\u2145', '\u2149'), + ('\u214e', '\u214e'), ('\u2160', '\u2182'), + ('\u2183', '\u2184'), ('\u2185', '\u2188'), + ('\u2c00', '\u2c2e'), ('\u2c30', '\u2c5e'), + ('\u2c60', '\u2c7b'), ('\u2c7c', '\u2c7d'), + ('\u2c7e', '\u2ce4'), ('\u2ceb', '\u2cee'), + ('\u2cef', '\u2cf1'), ('\u2cf2', '\u2cf3'), + ('\u2d00', '\u2d25'), ('\u2d27', '\u2d27'), + ('\u2d2d', '\u2d2d'), ('\u2d30', '\u2d67'), + ('\u2d6f', '\u2d6f'), ('\u2d7f', '\u2d7f'), + ('\u2d80', '\u2d96'), ('\u2da0', '\u2da6'), + ('\u2da8', '\u2dae'), ('\u2db0', '\u2db6'), + ('\u2db8', '\u2dbe'), ('\u2dc0', '\u2dc6'), + ('\u2dc8', '\u2dce'), ('\u2dd0', '\u2dd6'), + ('\u2dd8', '\u2dde'), ('\u2de0', '\u2dff'), + ('\u3005', '\u3005'), ('\u3006', '\u3006'), + ('\u3007', '\u3007'), ('\u3021', '\u3029'), + ('\u302a', '\u302d'), ('\u302e', '\u302f'), + ('\u3031', '\u3035'), ('\u3038', '\u303a'), + ('\u303b', '\u303b'), ('\u303c', '\u303c'), + ('\u3041', '\u3096'), ('\u3099', '\u309a'), + ('\u309d', '\u309e'), ('\u309f', '\u309f'), + ('\u30a1', '\u30fa'), ('\u30fc', '\u30fe'), + ('\u30ff', '\u30ff'), ('\u3105', '\u312d'), + ('\u3131', '\u318e'), ('\u31a0', '\u31ba'), + ('\u31f0', '\u31ff'), ('\u3400', '\u4db5'), + ('\u4e00', '\u9fcc'), ('\ua000', '\ua014'), + ('\ua015', '\ua015'), ('\ua016', '\ua48c'), + ('\ua4d0', '\ua4f7'), ('\ua4f8', '\ua4fd'), + ('\ua500', '\ua60b'), ('\ua60c', '\ua60c'), + ('\ua610', '\ua61f'), ('\ua620', '\ua629'), + ('\ua62a', '\ua62b'), ('\ua640', '\ua66d'), + ('\ua66e', '\ua66e'), ('\ua66f', '\ua66f'), + ('\ua674', '\ua67d'), ('\ua67f', '\ua67f'), + ('\ua680', '\ua697'), ('\ua69f', '\ua69f'), + ('\ua6a0', '\ua6e5'), ('\ua6e6', '\ua6ef'), + ('\ua6f0', '\ua6f1'), ('\ua717', '\ua71f'), + ('\ua722', '\ua76f'), ('\ua770', '\ua770'), + ('\ua771', '\ua787'), ('\ua788', '\ua788'), + ('\ua78b', '\ua78e'), ('\ua790', '\ua793'), + ('\ua7a0', '\ua7aa'), ('\ua7f8', '\ua7f9'), + ('\ua7fa', '\ua7fa'), ('\ua7fb', '\ua801'), + ('\ua802', '\ua802'), ('\ua803', '\ua805'), + ('\ua806', '\ua806'), ('\ua807', '\ua80a'), + ('\ua80b', '\ua80b'), ('\ua80c', '\ua822'), + ('\ua823', '\ua824'), ('\ua825', '\ua826'), + ('\ua827', '\ua827'), ('\ua840', '\ua873'), + ('\ua880', '\ua881'), ('\ua882', '\ua8b3'), + ('\ua8b4', '\ua8c3'), ('\ua8c4', '\ua8c4'), + ('\ua8d0', '\ua8d9'), ('\ua8e0', '\ua8f1'), + ('\ua8f2', '\ua8f7'), ('\ua8fb', '\ua8fb'), + ('\ua900', '\ua909'), ('\ua90a', '\ua925'), + ('\ua926', '\ua92d'), ('\ua930', '\ua946'), + ('\ua947', '\ua951'), ('\ua952', '\ua953'), + ('\ua960', '\ua97c'), ('\ua980', '\ua982'), + ('\ua983', '\ua983'), ('\ua984', '\ua9b2'), + ('\ua9b3', '\ua9b3'), ('\ua9b4', '\ua9b5'), + ('\ua9b6', '\ua9b9'), ('\ua9ba', '\ua9bb'), + ('\ua9bc', '\ua9bc'), ('\ua9bd', '\ua9c0'), + ('\ua9cf', '\ua9cf'), ('\ua9d0', '\ua9d9'), + ('\uaa00', '\uaa28'), ('\uaa29', '\uaa2e'), + ('\uaa2f', '\uaa30'), ('\uaa31', '\uaa32'), + ('\uaa33', '\uaa34'), ('\uaa35', '\uaa36'), + ('\uaa40', '\uaa42'), ('\uaa43', '\uaa43'), + ('\uaa44', '\uaa4b'), ('\uaa4c', '\uaa4c'), + ('\uaa4d', '\uaa4d'), ('\uaa50', '\uaa59'), + ('\uaa60', '\uaa6f'), ('\uaa70', '\uaa70'), + ('\uaa71', '\uaa76'), ('\uaa7a', '\uaa7a'), + ('\uaa7b', '\uaa7b'), ('\uaa80', '\uaaaf'), + ('\uaab0', '\uaab0'), ('\uaab1', '\uaab1'), + ('\uaab2', '\uaab4'), ('\uaab5', '\uaab6'), + ('\uaab7', '\uaab8'), ('\uaab9', '\uaabd'), + ('\uaabe', '\uaabf'), ('\uaac0', '\uaac0'), + ('\uaac1', '\uaac1'), ('\uaac2', '\uaac2'), + ('\uaadb', '\uaadc'), ('\uaadd', '\uaadd'), + ('\uaae0', '\uaaea'), ('\uaaeb', '\uaaeb'), + ('\uaaec', '\uaaed'), ('\uaaee', '\uaaef'), + ('\uaaf2', '\uaaf2'), ('\uaaf3', '\uaaf4'), + ('\uaaf5', '\uaaf5'), ('\uaaf6', '\uaaf6'), + ('\uab01', '\uab06'), ('\uab09', '\uab0e'), + ('\uab11', '\uab16'), ('\uab20', '\uab26'), + ('\uab28', '\uab2e'), ('\uabc0', '\uabe2'), + ('\uabe3', '\uabe4'), ('\uabe5', '\uabe5'), + ('\uabe6', '\uabe7'), ('\uabe8', '\uabe8'), + ('\uabe9', '\uabea'), ('\uabec', '\uabec'), + ('\uabed', '\uabed'), ('\uabf0', '\uabf9'), + ('\uac00', '\ud7a3'), ('\ud7b0', '\ud7c6'), + ('\ud7cb', '\ud7fb'), ('\uf900', '\ufa6d'), + ('\ufa70', '\ufad9'), ('\ufb00', '\ufb06'), + ('\ufb13', '\ufb17'), ('\ufb1d', '\ufb1d'), + ('\ufb1e', '\ufb1e'), ('\ufb1f', '\ufb28'), + ('\ufb2a', '\ufb36'), ('\ufb38', '\ufb3c'), + ('\ufb3e', '\ufb3e'), ('\ufb40', '\ufb41'), + ('\ufb43', '\ufb44'), ('\ufb46', '\ufbb1'), + ('\ufbd3', '\ufc5d'), ('\ufc64', '\ufd3d'), + ('\ufd50', '\ufd8f'), ('\ufd92', '\ufdc7'), + ('\ufdf0', '\ufdf9'), ('\ufe00', '\ufe0f'), + ('\ufe20', '\ufe26'), ('\ufe33', '\ufe34'), + ('\ufe4d', '\ufe4f'), ('\ufe71', '\ufe71'), + ('\ufe73', '\ufe73'), ('\ufe77', '\ufe77'), + ('\ufe79', '\ufe79'), ('\ufe7b', '\ufe7b'), + ('\ufe7d', '\ufe7d'), ('\ufe7f', '\ufefc'), + ('\uff10', '\uff19'), ('\uff21', '\uff3a'), + ('\uff3f', '\uff3f'), ('\uff41', '\uff5a'), + ('\uff66', '\uff6f'), ('\uff70', '\uff70'), + ('\uff71', '\uff9d'), ('\uff9e', '\uff9f'), + ('\uffa0', '\uffbe'), ('\uffc2', '\uffc7'), + ('\uffca', '\uffcf'), ('\uffd2', '\uffd7'), + ('\uffda', '\uffdc'), ('\U00010000', '\U0001000b'), + ('\U0001000d', '\U00010026'), ('\U00010028', '\U0001003a'), + ('\U0001003c', '\U0001003d'), ('\U0001003f', '\U0001004d'), + ('\U00010050', '\U0001005d'), ('\U00010080', '\U000100fa'), + ('\U00010140', '\U00010174'), ('\U000101fd', '\U000101fd'), + ('\U00010280', '\U0001029c'), ('\U000102a0', '\U000102d0'), + ('\U00010300', '\U0001031e'), ('\U00010330', '\U00010340'), + ('\U00010341', '\U00010341'), ('\U00010342', '\U00010349'), + ('\U0001034a', '\U0001034a'), ('\U00010380', '\U0001039d'), + ('\U000103a0', '\U000103c3'), ('\U000103c8', '\U000103cf'), + ('\U000103d1', '\U000103d5'), ('\U00010400', '\U0001044f'), + ('\U00010450', '\U0001049d'), ('\U000104a0', '\U000104a9'), + ('\U00010800', '\U00010805'), ('\U00010808', '\U00010808'), + ('\U0001080a', '\U00010835'), ('\U00010837', '\U00010838'), + ('\U0001083c', '\U0001083c'), ('\U0001083f', '\U00010855'), + ('\U00010900', '\U00010915'), ('\U00010920', '\U00010939'), + ('\U00010980', '\U000109b7'), ('\U000109be', '\U000109bf'), + ('\U00010a00', '\U00010a00'), ('\U00010a01', '\U00010a03'), + ('\U00010a05', '\U00010a06'), ('\U00010a0c', '\U00010a0f'), + ('\U00010a10', '\U00010a13'), ('\U00010a15', '\U00010a17'), + ('\U00010a19', '\U00010a33'), ('\U00010a38', '\U00010a3a'), + ('\U00010a3f', '\U00010a3f'), ('\U00010a60', '\U00010a7c'), + ('\U00010b00', '\U00010b35'), ('\U00010b40', '\U00010b55'), + ('\U00010b60', '\U00010b72'), ('\U00010c00', '\U00010c48'), + ('\U00011000', '\U00011000'), ('\U00011001', '\U00011001'), + ('\U00011002', '\U00011002'), ('\U00011003', '\U00011037'), + ('\U00011038', '\U00011046'), ('\U00011066', '\U0001106f'), + ('\U00011080', '\U00011081'), ('\U00011082', '\U00011082'), + ('\U00011083', '\U000110af'), ('\U000110b0', '\U000110b2'), + ('\U000110b3', '\U000110b6'), ('\U000110b7', '\U000110b8'), + ('\U000110b9', '\U000110ba'), ('\U000110d0', '\U000110e8'), + ('\U000110f0', '\U000110f9'), ('\U00011100', '\U00011102'), + ('\U00011103', '\U00011126'), ('\U00011127', '\U0001112b'), + ('\U0001112c', '\U0001112c'), ('\U0001112d', '\U00011134'), + ('\U00011136', '\U0001113f'), ('\U00011180', '\U00011181'), + ('\U00011182', '\U00011182'), ('\U00011183', '\U000111b2'), + ('\U000111b3', '\U000111b5'), ('\U000111b6', '\U000111be'), + ('\U000111bf', '\U000111c0'), ('\U000111c1', '\U000111c4'), + ('\U000111d0', '\U000111d9'), ('\U00011680', '\U000116aa'), + ('\U000116ab', '\U000116ab'), ('\U000116ac', '\U000116ac'), + ('\U000116ad', '\U000116ad'), ('\U000116ae', '\U000116af'), + ('\U000116b0', '\U000116b5'), ('\U000116b6', '\U000116b6'), + ('\U000116b7', '\U000116b7'), ('\U000116c0', '\U000116c9'), + ('\U00012000', '\U0001236e'), ('\U00012400', '\U00012462'), + ('\U00013000', '\U0001342e'), ('\U00016800', '\U00016a38'), + ('\U00016f00', '\U00016f44'), ('\U00016f50', '\U00016f50'), + ('\U00016f51', '\U00016f7e'), ('\U00016f8f', '\U00016f92'), + ('\U00016f93', '\U00016f9f'), ('\U0001b000', '\U0001b001'), + ('\U0001d165', '\U0001d166'), ('\U0001d167', '\U0001d169'), + ('\U0001d16d', '\U0001d172'), ('\U0001d17b', '\U0001d182'), + ('\U0001d185', '\U0001d18b'), ('\U0001d1aa', '\U0001d1ad'), + ('\U0001d242', '\U0001d244'), ('\U0001d400', '\U0001d454'), + ('\U0001d456', '\U0001d49c'), ('\U0001d49e', '\U0001d49f'), + ('\U0001d4a2', '\U0001d4a2'), ('\U0001d4a5', '\U0001d4a6'), + ('\U0001d4a9', '\U0001d4ac'), ('\U0001d4ae', '\U0001d4b9'), + ('\U0001d4bb', '\U0001d4bb'), ('\U0001d4bd', '\U0001d4c3'), + ('\U0001d4c5', '\U0001d505'), ('\U0001d507', '\U0001d50a'), + ('\U0001d50d', '\U0001d514'), ('\U0001d516', '\U0001d51c'), + ('\U0001d51e', '\U0001d539'), ('\U0001d53b', '\U0001d53e'), + ('\U0001d540', '\U0001d544'), ('\U0001d546', '\U0001d546'), + ('\U0001d54a', '\U0001d550'), ('\U0001d552', '\U0001d6a5'), + ('\U0001d6a8', '\U0001d6c0'), ('\U0001d6c2', '\U0001d6da'), + ('\U0001d6dc', '\U0001d6fa'), ('\U0001d6fc', '\U0001d714'), + ('\U0001d716', '\U0001d734'), ('\U0001d736', '\U0001d74e'), + ('\U0001d750', '\U0001d76e'), ('\U0001d770', '\U0001d788'), + ('\U0001d78a', '\U0001d7a8'), ('\U0001d7aa', '\U0001d7c2'), + ('\U0001d7c4', '\U0001d7cb'), ('\U0001d7ce', '\U0001d7ff'), + ('\U0001ee00', '\U0001ee03'), ('\U0001ee05', '\U0001ee1f'), + ('\U0001ee21', '\U0001ee22'), ('\U0001ee24', '\U0001ee24'), + ('\U0001ee27', '\U0001ee27'), ('\U0001ee29', '\U0001ee32'), + ('\U0001ee34', '\U0001ee37'), ('\U0001ee39', '\U0001ee39'), + ('\U0001ee3b', '\U0001ee3b'), ('\U0001ee42', '\U0001ee42'), + ('\U0001ee47', '\U0001ee47'), ('\U0001ee49', '\U0001ee49'), + ('\U0001ee4b', '\U0001ee4b'), ('\U0001ee4d', '\U0001ee4f'), + ('\U0001ee51', '\U0001ee52'), ('\U0001ee54', '\U0001ee54'), + ('\U0001ee57', '\U0001ee57'), ('\U0001ee59', '\U0001ee59'), + ('\U0001ee5b', '\U0001ee5b'), ('\U0001ee5d', '\U0001ee5d'), + ('\U0001ee5f', '\U0001ee5f'), ('\U0001ee61', '\U0001ee62'), + ('\U0001ee64', '\U0001ee64'), ('\U0001ee67', '\U0001ee6a'), + ('\U0001ee6c', '\U0001ee72'), ('\U0001ee74', '\U0001ee77'), + ('\U0001ee79', '\U0001ee7c'), ('\U0001ee7e', '\U0001ee7e'), + ('\U0001ee80', '\U0001ee89'), ('\U0001ee8b', '\U0001ee9b'), + ('\U0001eea1', '\U0001eea3'), ('\U0001eea5', '\U0001eea9'), + ('\U0001eeab', '\U0001eebb'), ('\U00020000', '\U0002a6d6'), + ('\U0002a700', '\U0002b734'), ('\U0002b740', '\U0002b81d'), + ('\U0002f800', '\U0002fa1d'), ('\U000e0100', '\U000e01ef') + ]; + + pub fn XID_Continue(c: char) -> bool { + bsearch_range_table(c, XID_Continue_table) } - #[test] - fn test_is_upper() { - assert!((unicode::icu::is_upper('M'))); - assert!((!unicode::icu::is_upper('m'))); + static XID_Start_table : &'static [(char,char)] = &[ + ('\x41', '\x5a'), ('\x61', '\x7a'), + ('\xaa', '\xaa'), ('\xb5', '\xb5'), + ('\xba', '\xba'), ('\xc0', '\xd6'), + ('\xd8', '\xf6'), ('\xf8', '\u01ba'), + ('\u01bb', '\u01bb'), ('\u01bc', '\u01bf'), + ('\u01c0', '\u01c3'), ('\u01c4', '\u0293'), + ('\u0294', '\u0294'), ('\u0295', '\u02af'), + ('\u02b0', '\u02c1'), ('\u02c6', '\u02d1'), + ('\u02e0', '\u02e4'), ('\u02ec', '\u02ec'), + ('\u02ee', '\u02ee'), ('\u0370', '\u0373'), + ('\u0374', '\u0374'), ('\u0376', '\u0377'), + ('\u037b', '\u037d'), ('\u0386', '\u0386'), + ('\u0388', '\u038a'), ('\u038c', '\u038c'), + ('\u038e', '\u03a1'), ('\u03a3', '\u03f5'), + ('\u03f7', '\u0481'), ('\u048a', '\u0527'), + ('\u0531', '\u0556'), ('\u0559', '\u0559'), + ('\u0561', '\u0587'), ('\u05d0', '\u05ea'), + ('\u05f0', '\u05f2'), ('\u0620', '\u063f'), + ('\u0640', '\u0640'), ('\u0641', '\u064a'), + ('\u066e', '\u066f'), ('\u0671', '\u06d3'), + ('\u06d5', '\u06d5'), ('\u06e5', '\u06e6'), + ('\u06ee', '\u06ef'), ('\u06fa', '\u06fc'), + ('\u06ff', '\u06ff'), ('\u0710', '\u0710'), + ('\u0712', '\u072f'), ('\u074d', '\u07a5'), + ('\u07b1', '\u07b1'), ('\u07ca', '\u07ea'), + ('\u07f4', '\u07f5'), ('\u07fa', '\u07fa'), + ('\u0800', '\u0815'), ('\u081a', '\u081a'), + ('\u0824', '\u0824'), ('\u0828', '\u0828'), + ('\u0840', '\u0858'), ('\u08a0', '\u08a0'), + ('\u08a2', '\u08ac'), ('\u0904', '\u0939'), + ('\u093d', '\u093d'), ('\u0950', '\u0950'), + ('\u0958', '\u0961'), ('\u0971', '\u0971'), + ('\u0972', '\u0977'), ('\u0979', '\u097f'), + ('\u0985', '\u098c'), ('\u098f', '\u0990'), + ('\u0993', '\u09a8'), ('\u09aa', '\u09b0'), + ('\u09b2', '\u09b2'), ('\u09b6', '\u09b9'), + ('\u09bd', '\u09bd'), ('\u09ce', '\u09ce'), + ('\u09dc', '\u09dd'), ('\u09df', '\u09e1'), + ('\u09f0', '\u09f1'), ('\u0a05', '\u0a0a'), + ('\u0a0f', '\u0a10'), ('\u0a13', '\u0a28'), + ('\u0a2a', '\u0a30'), ('\u0a32', '\u0a33'), + ('\u0a35', '\u0a36'), ('\u0a38', '\u0a39'), + ('\u0a59', '\u0a5c'), ('\u0a5e', '\u0a5e'), + ('\u0a72', '\u0a74'), ('\u0a85', '\u0a8d'), + ('\u0a8f', '\u0a91'), ('\u0a93', '\u0aa8'), + ('\u0aaa', '\u0ab0'), ('\u0ab2', '\u0ab3'), + ('\u0ab5', '\u0ab9'), ('\u0abd', '\u0abd'), + ('\u0ad0', '\u0ad0'), ('\u0ae0', '\u0ae1'), + ('\u0b05', '\u0b0c'), ('\u0b0f', '\u0b10'), + ('\u0b13', '\u0b28'), ('\u0b2a', '\u0b30'), + ('\u0b32', '\u0b33'), ('\u0b35', '\u0b39'), + ('\u0b3d', '\u0b3d'), ('\u0b5c', '\u0b5d'), + ('\u0b5f', '\u0b61'), ('\u0b71', '\u0b71'), + ('\u0b83', '\u0b83'), ('\u0b85', '\u0b8a'), + ('\u0b8e', '\u0b90'), ('\u0b92', '\u0b95'), + ('\u0b99', '\u0b9a'), ('\u0b9c', '\u0b9c'), + ('\u0b9e', '\u0b9f'), ('\u0ba3', '\u0ba4'), + ('\u0ba8', '\u0baa'), ('\u0bae', '\u0bb9'), + ('\u0bd0', '\u0bd0'), ('\u0c05', '\u0c0c'), + ('\u0c0e', '\u0c10'), ('\u0c12', '\u0c28'), + ('\u0c2a', '\u0c33'), ('\u0c35', '\u0c39'), + ('\u0c3d', '\u0c3d'), ('\u0c58', '\u0c59'), + ('\u0c60', '\u0c61'), ('\u0c85', '\u0c8c'), + ('\u0c8e', '\u0c90'), ('\u0c92', '\u0ca8'), + ('\u0caa', '\u0cb3'), ('\u0cb5', '\u0cb9'), + ('\u0cbd', '\u0cbd'), ('\u0cde', '\u0cde'), + ('\u0ce0', '\u0ce1'), ('\u0cf1', '\u0cf2'), + ('\u0d05', '\u0d0c'), ('\u0d0e', '\u0d10'), + ('\u0d12', '\u0d3a'), ('\u0d3d', '\u0d3d'), + ('\u0d4e', '\u0d4e'), ('\u0d60', '\u0d61'), + ('\u0d7a', '\u0d7f'), ('\u0d85', '\u0d96'), + ('\u0d9a', '\u0db1'), ('\u0db3', '\u0dbb'), + ('\u0dbd', '\u0dbd'), ('\u0dc0', '\u0dc6'), + ('\u0e01', '\u0e30'), ('\u0e32', '\u0e32'), + ('\u0e40', '\u0e45'), ('\u0e46', '\u0e46'), + ('\u0e81', '\u0e82'), ('\u0e84', '\u0e84'), + ('\u0e87', '\u0e88'), ('\u0e8a', '\u0e8a'), + ('\u0e8d', '\u0e8d'), ('\u0e94', '\u0e97'), + ('\u0e99', '\u0e9f'), ('\u0ea1', '\u0ea3'), + ('\u0ea5', '\u0ea5'), ('\u0ea7', '\u0ea7'), + ('\u0eaa', '\u0eab'), ('\u0ead', '\u0eb0'), + ('\u0eb2', '\u0eb2'), ('\u0ebd', '\u0ebd'), + ('\u0ec0', '\u0ec4'), ('\u0ec6', '\u0ec6'), + ('\u0edc', '\u0edf'), ('\u0f00', '\u0f00'), + ('\u0f40', '\u0f47'), ('\u0f49', '\u0f6c'), + ('\u0f88', '\u0f8c'), ('\u1000', '\u102a'), + ('\u103f', '\u103f'), ('\u1050', '\u1055'), + ('\u105a', '\u105d'), ('\u1061', '\u1061'), + ('\u1065', '\u1066'), ('\u106e', '\u1070'), + ('\u1075', '\u1081'), ('\u108e', '\u108e'), + ('\u10a0', '\u10c5'), ('\u10c7', '\u10c7'), + ('\u10cd', '\u10cd'), ('\u10d0', '\u10fa'), + ('\u10fc', '\u10fc'), ('\u10fd', '\u1248'), + ('\u124a', '\u124d'), ('\u1250', '\u1256'), + ('\u1258', '\u1258'), ('\u125a', '\u125d'), + ('\u1260', '\u1288'), ('\u128a', '\u128d'), + ('\u1290', '\u12b0'), ('\u12b2', '\u12b5'), + ('\u12b8', '\u12be'), ('\u12c0', '\u12c0'), + ('\u12c2', '\u12c5'), ('\u12c8', '\u12d6'), + ('\u12d8', '\u1310'), ('\u1312', '\u1315'), + ('\u1318', '\u135a'), ('\u1380', '\u138f'), + ('\u13a0', '\u13f4'), ('\u1401', '\u166c'), + ('\u166f', '\u167f'), ('\u1681', '\u169a'), + ('\u16a0', '\u16ea'), ('\u16ee', '\u16f0'), + ('\u1700', '\u170c'), ('\u170e', '\u1711'), + ('\u1720', '\u1731'), ('\u1740', '\u1751'), + ('\u1760', '\u176c'), ('\u176e', '\u1770'), + ('\u1780', '\u17b3'), ('\u17d7', '\u17d7'), + ('\u17dc', '\u17dc'), ('\u1820', '\u1842'), + ('\u1843', '\u1843'), ('\u1844', '\u1877'), + ('\u1880', '\u18a8'), ('\u18aa', '\u18aa'), + ('\u18b0', '\u18f5'), ('\u1900', '\u191c'), + ('\u1950', '\u196d'), ('\u1970', '\u1974'), + ('\u1980', '\u19ab'), ('\u19c1', '\u19c7'), + ('\u1a00', '\u1a16'), ('\u1a20', '\u1a54'), + ('\u1aa7', '\u1aa7'), ('\u1b05', '\u1b33'), + ('\u1b45', '\u1b4b'), ('\u1b83', '\u1ba0'), + ('\u1bae', '\u1baf'), ('\u1bba', '\u1be5'), + ('\u1c00', '\u1c23'), ('\u1c4d', '\u1c4f'), + ('\u1c5a', '\u1c77'), ('\u1c78', '\u1c7d'), + ('\u1ce9', '\u1cec'), ('\u1cee', '\u1cf1'), + ('\u1cf5', '\u1cf6'), ('\u1d00', '\u1d2b'), + ('\u1d2c', '\u1d6a'), ('\u1d6b', '\u1d77'), + ('\u1d78', '\u1d78'), ('\u1d79', '\u1d9a'), + ('\u1d9b', '\u1dbf'), ('\u1e00', '\u1f15'), + ('\u1f18', '\u1f1d'), ('\u1f20', '\u1f45'), + ('\u1f48', '\u1f4d'), ('\u1f50', '\u1f57'), + ('\u1f59', '\u1f59'), ('\u1f5b', '\u1f5b'), + ('\u1f5d', '\u1f5d'), ('\u1f5f', '\u1f7d'), + ('\u1f80', '\u1fb4'), ('\u1fb6', '\u1fbc'), + ('\u1fbe', '\u1fbe'), ('\u1fc2', '\u1fc4'), + ('\u1fc6', '\u1fcc'), ('\u1fd0', '\u1fd3'), + ('\u1fd6', '\u1fdb'), ('\u1fe0', '\u1fec'), + ('\u1ff2', '\u1ff4'), ('\u1ff6', '\u1ffc'), + ('\u2071', '\u2071'), ('\u207f', '\u207f'), + ('\u2090', '\u209c'), ('\u2102', '\u2102'), + ('\u2107', '\u2107'), ('\u210a', '\u2113'), + ('\u2115', '\u2115'), ('\u2118', '\u2118'), + ('\u2119', '\u211d'), ('\u2124', '\u2124'), + ('\u2126', '\u2126'), ('\u2128', '\u2128'), + ('\u212a', '\u212d'), ('\u212e', '\u212e'), + ('\u212f', '\u2134'), ('\u2135', '\u2138'), + ('\u2139', '\u2139'), ('\u213c', '\u213f'), + ('\u2145', '\u2149'), ('\u214e', '\u214e'), + ('\u2160', '\u2182'), ('\u2183', '\u2184'), + ('\u2185', '\u2188'), ('\u2c00', '\u2c2e'), + ('\u2c30', '\u2c5e'), ('\u2c60', '\u2c7b'), + ('\u2c7c', '\u2c7d'), ('\u2c7e', '\u2ce4'), + ('\u2ceb', '\u2cee'), ('\u2cf2', '\u2cf3'), + ('\u2d00', '\u2d25'), ('\u2d27', '\u2d27'), + ('\u2d2d', '\u2d2d'), ('\u2d30', '\u2d67'), + ('\u2d6f', '\u2d6f'), ('\u2d80', '\u2d96'), + ('\u2da0', '\u2da6'), ('\u2da8', '\u2dae'), + ('\u2db0', '\u2db6'), ('\u2db8', '\u2dbe'), + ('\u2dc0', '\u2dc6'), ('\u2dc8', '\u2dce'), + ('\u2dd0', '\u2dd6'), ('\u2dd8', '\u2dde'), + ('\u3005', '\u3005'), ('\u3006', '\u3006'), + ('\u3007', '\u3007'), ('\u3021', '\u3029'), + ('\u3031', '\u3035'), ('\u3038', '\u303a'), + ('\u303b', '\u303b'), ('\u303c', '\u303c'), + ('\u3041', '\u3096'), ('\u309d', '\u309e'), + ('\u309f', '\u309f'), ('\u30a1', '\u30fa'), + ('\u30fc', '\u30fe'), ('\u30ff', '\u30ff'), + ('\u3105', '\u312d'), ('\u3131', '\u318e'), + ('\u31a0', '\u31ba'), ('\u31f0', '\u31ff'), + ('\u3400', '\u4db5'), ('\u4e00', '\u9fcc'), + ('\ua000', '\ua014'), ('\ua015', '\ua015'), + ('\ua016', '\ua48c'), ('\ua4d0', '\ua4f7'), + ('\ua4f8', '\ua4fd'), ('\ua500', '\ua60b'), + ('\ua60c', '\ua60c'), ('\ua610', '\ua61f'), + ('\ua62a', '\ua62b'), ('\ua640', '\ua66d'), + ('\ua66e', '\ua66e'), ('\ua67f', '\ua67f'), + ('\ua680', '\ua697'), ('\ua6a0', '\ua6e5'), + ('\ua6e6', '\ua6ef'), ('\ua717', '\ua71f'), + ('\ua722', '\ua76f'), ('\ua770', '\ua770'), + ('\ua771', '\ua787'), ('\ua788', '\ua788'), + ('\ua78b', '\ua78e'), ('\ua790', '\ua793'), + ('\ua7a0', '\ua7aa'), ('\ua7f8', '\ua7f9'), + ('\ua7fa', '\ua7fa'), ('\ua7fb', '\ua801'), + ('\ua803', '\ua805'), ('\ua807', '\ua80a'), + ('\ua80c', '\ua822'), ('\ua840', '\ua873'), + ('\ua882', '\ua8b3'), ('\ua8f2', '\ua8f7'), + ('\ua8fb', '\ua8fb'), ('\ua90a', '\ua925'), + ('\ua930', '\ua946'), ('\ua960', '\ua97c'), + ('\ua984', '\ua9b2'), ('\ua9cf', '\ua9cf'), + ('\uaa00', '\uaa28'), ('\uaa40', '\uaa42'), + ('\uaa44', '\uaa4b'), ('\uaa60', '\uaa6f'), + ('\uaa70', '\uaa70'), ('\uaa71', '\uaa76'), + ('\uaa7a', '\uaa7a'), ('\uaa80', '\uaaaf'), + ('\uaab1', '\uaab1'), ('\uaab5', '\uaab6'), + ('\uaab9', '\uaabd'), ('\uaac0', '\uaac0'), + ('\uaac2', '\uaac2'), ('\uaadb', '\uaadc'), + ('\uaadd', '\uaadd'), ('\uaae0', '\uaaea'), + ('\uaaf2', '\uaaf2'), ('\uaaf3', '\uaaf4'), + ('\uab01', '\uab06'), ('\uab09', '\uab0e'), + ('\uab11', '\uab16'), ('\uab20', '\uab26'), + ('\uab28', '\uab2e'), ('\uabc0', '\uabe2'), + ('\uac00', '\ud7a3'), ('\ud7b0', '\ud7c6'), + ('\ud7cb', '\ud7fb'), ('\uf900', '\ufa6d'), + ('\ufa70', '\ufad9'), ('\ufb00', '\ufb06'), + ('\ufb13', '\ufb17'), ('\ufb1d', '\ufb1d'), + ('\ufb1f', '\ufb28'), ('\ufb2a', '\ufb36'), + ('\ufb38', '\ufb3c'), ('\ufb3e', '\ufb3e'), + ('\ufb40', '\ufb41'), ('\ufb43', '\ufb44'), + ('\ufb46', '\ufbb1'), ('\ufbd3', '\ufc5d'), + ('\ufc64', '\ufd3d'), ('\ufd50', '\ufd8f'), + ('\ufd92', '\ufdc7'), ('\ufdf0', '\ufdf9'), + ('\ufe71', '\ufe71'), ('\ufe73', '\ufe73'), + ('\ufe77', '\ufe77'), ('\ufe79', '\ufe79'), + ('\ufe7b', '\ufe7b'), ('\ufe7d', '\ufe7d'), + ('\ufe7f', '\ufefc'), ('\uff21', '\uff3a'), + ('\uff41', '\uff5a'), ('\uff66', '\uff6f'), + ('\uff70', '\uff70'), ('\uff71', '\uff9d'), + ('\uffa0', '\uffbe'), ('\uffc2', '\uffc7'), + ('\uffca', '\uffcf'), ('\uffd2', '\uffd7'), + ('\uffda', '\uffdc'), ('\U00010000', '\U0001000b'), + ('\U0001000d', '\U00010026'), ('\U00010028', '\U0001003a'), + ('\U0001003c', '\U0001003d'), ('\U0001003f', '\U0001004d'), + ('\U00010050', '\U0001005d'), ('\U00010080', '\U000100fa'), + ('\U00010140', '\U00010174'), ('\U00010280', '\U0001029c'), + ('\U000102a0', '\U000102d0'), ('\U00010300', '\U0001031e'), + ('\U00010330', '\U00010340'), ('\U00010341', '\U00010341'), + ('\U00010342', '\U00010349'), ('\U0001034a', '\U0001034a'), + ('\U00010380', '\U0001039d'), ('\U000103a0', '\U000103c3'), + ('\U000103c8', '\U000103cf'), ('\U000103d1', '\U000103d5'), + ('\U00010400', '\U0001044f'), ('\U00010450', '\U0001049d'), + ('\U00010800', '\U00010805'), ('\U00010808', '\U00010808'), + ('\U0001080a', '\U00010835'), ('\U00010837', '\U00010838'), + ('\U0001083c', '\U0001083c'), ('\U0001083f', '\U00010855'), + ('\U00010900', '\U00010915'), ('\U00010920', '\U00010939'), + ('\U00010980', '\U000109b7'), ('\U000109be', '\U000109bf'), + ('\U00010a00', '\U00010a00'), ('\U00010a10', '\U00010a13'), + ('\U00010a15', '\U00010a17'), ('\U00010a19', '\U00010a33'), + ('\U00010a60', '\U00010a7c'), ('\U00010b00', '\U00010b35'), + ('\U00010b40', '\U00010b55'), ('\U00010b60', '\U00010b72'), + ('\U00010c00', '\U00010c48'), ('\U00011003', '\U00011037'), + ('\U00011083', '\U000110af'), ('\U000110d0', '\U000110e8'), + ('\U00011103', '\U00011126'), ('\U00011183', '\U000111b2'), + ('\U000111c1', '\U000111c4'), ('\U00011680', '\U000116aa'), + ('\U00012000', '\U0001236e'), ('\U00012400', '\U00012462'), + ('\U00013000', '\U0001342e'), ('\U00016800', '\U00016a38'), + ('\U00016f00', '\U00016f44'), ('\U00016f50', '\U00016f50'), + ('\U00016f93', '\U00016f9f'), ('\U0001b000', '\U0001b001'), + ('\U0001d400', '\U0001d454'), ('\U0001d456', '\U0001d49c'), + ('\U0001d49e', '\U0001d49f'), ('\U0001d4a2', '\U0001d4a2'), + ('\U0001d4a5', '\U0001d4a6'), ('\U0001d4a9', '\U0001d4ac'), + ('\U0001d4ae', '\U0001d4b9'), ('\U0001d4bb', '\U0001d4bb'), + ('\U0001d4bd', '\U0001d4c3'), ('\U0001d4c5', '\U0001d505'), + ('\U0001d507', '\U0001d50a'), ('\U0001d50d', '\U0001d514'), + ('\U0001d516', '\U0001d51c'), ('\U0001d51e', '\U0001d539'), + ('\U0001d53b', '\U0001d53e'), ('\U0001d540', '\U0001d544'), + ('\U0001d546', '\U0001d546'), ('\U0001d54a', '\U0001d550'), + ('\U0001d552', '\U0001d6a5'), ('\U0001d6a8', '\U0001d6c0'), + ('\U0001d6c2', '\U0001d6da'), ('\U0001d6dc', '\U0001d6fa'), + ('\U0001d6fc', '\U0001d714'), ('\U0001d716', '\U0001d734'), + ('\U0001d736', '\U0001d74e'), ('\U0001d750', '\U0001d76e'), + ('\U0001d770', '\U0001d788'), ('\U0001d78a', '\U0001d7a8'), + ('\U0001d7aa', '\U0001d7c2'), ('\U0001d7c4', '\U0001d7cb'), + ('\U0001ee00', '\U0001ee03'), ('\U0001ee05', '\U0001ee1f'), + ('\U0001ee21', '\U0001ee22'), ('\U0001ee24', '\U0001ee24'), + ('\U0001ee27', '\U0001ee27'), ('\U0001ee29', '\U0001ee32'), + ('\U0001ee34', '\U0001ee37'), ('\U0001ee39', '\U0001ee39'), + ('\U0001ee3b', '\U0001ee3b'), ('\U0001ee42', '\U0001ee42'), + ('\U0001ee47', '\U0001ee47'), ('\U0001ee49', '\U0001ee49'), + ('\U0001ee4b', '\U0001ee4b'), ('\U0001ee4d', '\U0001ee4f'), + ('\U0001ee51', '\U0001ee52'), ('\U0001ee54', '\U0001ee54'), + ('\U0001ee57', '\U0001ee57'), ('\U0001ee59', '\U0001ee59'), + ('\U0001ee5b', '\U0001ee5b'), ('\U0001ee5d', '\U0001ee5d'), + ('\U0001ee5f', '\U0001ee5f'), ('\U0001ee61', '\U0001ee62'), + ('\U0001ee64', '\U0001ee64'), ('\U0001ee67', '\U0001ee6a'), + ('\U0001ee6c', '\U0001ee72'), ('\U0001ee74', '\U0001ee77'), + ('\U0001ee79', '\U0001ee7c'), ('\U0001ee7e', '\U0001ee7e'), + ('\U0001ee80', '\U0001ee89'), ('\U0001ee8b', '\U0001ee9b'), + ('\U0001eea1', '\U0001eea3'), ('\U0001eea5', '\U0001eea9'), + ('\U0001eeab', '\U0001eebb'), ('\U00020000', '\U0002a6d6'), + ('\U0002a700', '\U0002b734'), ('\U0002b740', '\U0002b81d'), + ('\U0002f800', '\U0002fa1d') + ]; + + pub fn XID_Start(c: char) -> bool { + bsearch_range_table(c, XID_Start_table) } } diff --git a/src/libstd/unstable/at_exit.rs b/src/libstd/unstable/at_exit.rs new file mode 100644 index 00000000000..d214b509dfb --- /dev/null +++ b/src/libstd/unstable/at_exit.rs @@ -0,0 +1,100 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 cast; +use libc::size_t; +use rand::RngUtil; +use rand; +use sys; +use task; +use vec; + +#[cfg(test)] use uint; + +/** +Register a function to be run during runtime shutdown. + +After all non-weak tasks have exited, registered exit functions will +execute, in random order, on the primary scheduler. Each function runs +in its own unsupervised task. +*/ +pub fn at_exit(f: ~fn()) { + unsafe { + let runner: &fn(*ExitFunctions) = exit_runner; + let runner_pair: sys::Closure = cast::transmute(runner); + let runner_ptr = runner_pair.code; + let runner_ptr = cast::transmute(runner_ptr); + rustrt::rust_register_exit_function(runner_ptr, ~f); + } +} + +// NB: The double pointer indirection here is because ~fn() is a fat +// pointer and due to FFI problems I am more comfortable making the +// interface use a normal pointer +mod rustrt { + use libc::c_void; + + pub extern { + fn rust_register_exit_function(runner: *c_void, f: ~~fn()); + } +} + +struct ExitFunctions { + // The number of exit functions + count: size_t, + // The buffer of exit functions + start: *~~fn() +} + +fn exit_runner(exit_fns: *ExitFunctions) { + let exit_fns = unsafe { &*exit_fns }; + let count = (*exit_fns).count; + let start = (*exit_fns).start; + + // NB: from_buf memcpys from the source, which will + // give us ownership of the array of functions + let mut exit_fns_vec = unsafe { vec::from_buf(start, count as uint) }; + // Let's not make any promises about execution order + let mut rng = rand::rng(); + rng.shuffle_mut(exit_fns_vec); + + debug!("running %u exit functions", exit_fns_vec.len()); + + while !exit_fns_vec.is_empty() { + match exit_fns_vec.pop() { + ~f => { + let mut task = task::task(); + task.supervised(); + task.spawn(f); + } + } + } +} + +#[test] +fn test_at_exit() { + let i = 10; + do at_exit { + debug!("at_exit1"); + assert_eq!(i, 10); + } +} + +#[test] +fn test_at_exit_many() { + let i = 10; + for uint::range(20, 100) |j| { + do at_exit { + debug!("at_exit2"); + assert_eq!(i, 10); + assert!(j > i); + } + } +} diff --git a/src/libstd/unstable/exchange_alloc.rs b/src/libstd/unstable/exchange_alloc.rs new file mode 100644 index 00000000000..3b35c2fb804 --- /dev/null +++ b/src/libstd/unstable/exchange_alloc.rs @@ -0,0 +1,83 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 sys::{TypeDesc, size_of}; +use libc::{c_void, size_t}; +use c_malloc = libc::malloc; +use c_free = libc::free; +use managed::raw::{BoxHeaderRepr, BoxRepr}; +use cast::transmute; +use unstable::intrinsics::{atomic_xadd,atomic_xsub}; +use ptr::null; +use intrinsic::TyDesc; + +pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void { + assert!(td.is_not_null()); + + let total_size = get_box_size(size, (*td).align); + let p = c_malloc(total_size as size_t); + assert!(p.is_not_null()); + + // FIXME #3475: Converting between our two different tydesc types + let td: *TyDesc = transmute(td); + + let box: &mut BoxRepr = transmute(p); + box.header.ref_count = -1; // Exchange values not ref counted + box.header.type_desc = td; + box.header.prev = null(); + box.header.next = null(); + + let exchange_count = &mut *rust_get_exchange_count_ptr(); + atomic_xadd(exchange_count, 1); + + return transmute(box); +} +/** +Thin wrapper around libc::malloc, none of the box header +stuff in exchange_alloc::malloc +*/ +pub unsafe fn malloc_raw(size: uint) -> *c_void { + let p = c_malloc(size as size_t); + if p.is_null() { + fail!("Failure in malloc_raw: result ptr is null"); + } + p +} + +pub unsafe fn free(ptr: *c_void) { + let exchange_count = &mut *rust_get_exchange_count_ptr(); + atomic_xsub(exchange_count, 1); + + assert!(ptr.is_not_null()); + c_free(ptr); +} +///Thin wrapper around libc::free, as with exchange_alloc::malloc_raw +pub unsafe fn free_raw(ptr: *c_void) { + c_free(ptr); +} + +fn get_box_size(body_size: uint, body_align: uint) -> uint { + let header_size = size_of::<BoxHeaderRepr>(); + // FIXME (#2699): This alignment calculation is suspicious. Is it right? + let total_size = align_to(header_size, body_align) + body_size; + return total_size; +} + +// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power +// of two. +fn align_to(size: uint, align: uint) -> uint { + assert!(align != 0); + (size + align - 1) & !(align - 1) +} + +extern { + #[rust_stack] + fn rust_get_exchange_count_ptr() -> *mut int; +} diff --git a/src/libstd/unstable/extfmt.rs b/src/libstd/unstable/extfmt.rs new file mode 100644 index 00000000000..8da378fdc97 --- /dev/null +++ b/src/libstd/unstable/extfmt.rs @@ -0,0 +1,690 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Support for fmt! expressions. +//! +//! The syntax is close to that of Posix format strings: +//! +//! ~~~~~~ +//! Format := '%' Parameter? Flag* Width? Precision? Type +//! Parameter := [0-9]+ '$' +//! Flag := [ 0#+-] +//! Width := Parameter | [0-9]+ +//! Precision := '.' [0-9]+ +//! Type := [bcdfiostuxX?] +//! ~~~~~~ +//! +//! * Parameter is the 1-based argument to apply the format to. Currently not +//! implemented. +//! * Flag 0 causes leading zeros to be used for padding when converting +//! numbers. +//! * Flag # causes the conversion to be done in an *alternative* manner. +//! Currently not implemented. +//! * Flag + causes signed numbers to always be prepended with a sign +//! character. +//! * Flag - left justifies the result +//! * Width specifies the minimum field width of the result. By default +//! leading spaces are added. +//! * Precision specifies the minimum number of digits for integral types +//! and the minimum number +//! of decimal places for float. +//! +//! The types currently supported are: +//! +//! * b - bool +//! * c - char +//! * d - int +//! * f - float +//! * i - int (same as d) +//! * o - uint as octal +//! * t - uint as binary +//! * u - uint +//! * x - uint as lower-case hexadecimal +//! * X - uint as upper-case hexadecimal +//! * s - str (any flavor) +//! * ? - arbitrary type (does not use the to_str trait) + +/* +Syntax Extension: fmt + +Format a string + +The 'fmt' extension is modeled on the posix printf system. + +A posix conversion ostensibly looks like this + +> %~[parameter]~[flags]~[width]~[.precision]~[length]type + +Given the different numeric type bestiary we have, we omit the 'length' +parameter and support slightly different conversions for 'type' + +> %~[parameter]~[flags]~[width]~[.precision]type + +we also only support translating-to-rust a tiny subset of the possible +combinations at the moment. + +Example: + +debug!("hello, %s!", "world"); + +*/ + +use cmp::Eq; +use prelude::*; + +/* + * We have a 'ct' (compile-time) module that parses format strings into a + * sequence of conversions. From those conversions AST fragments are built + * that call into properly-typed functions in the 'rt' (run-time) module. + * Each of those run-time conversion functions accepts another conversion + * description that specifies how to format its output. + * + * The building of the AST is currently done in a module inside the compiler, + * but should migrate over here as the plugin interface is defined. + */ + +// Functions used by the fmt extension at compile time +#[doc(hidden)] +pub mod ct { + use char; + use prelude::*; + use str; + + #[deriving(Eq)] + pub enum Signedness { Signed, Unsigned, } + + #[deriving(Eq)] + pub enum Caseness { CaseUpper, CaseLower, } + + #[deriving(Eq)] + pub enum Ty { + TyBool, + TyStr, + TyChar, + TyInt(Signedness), + TyBits, + TyHex(Caseness), + TyOctal, + TyFloat, + TyPoly, + } + + #[deriving(Eq)] + pub enum Flag { + FlagLeftJustify, + FlagLeftZeroPad, + FlagSpaceForSign, + FlagSignAlways, + FlagAlternate, + } + + #[deriving(Eq)] + pub enum Count { + CountIs(uint), + CountIsParam(uint), + CountIsNextParam, + CountImplied, + } + + #[deriving(Eq)] + struct Parsed<T> { + val: T, + next: uint + } + + pub impl<T> Parsed<T> { + fn new(val: T, next: uint) -> Parsed<T> { + Parsed {val: val, next: next} + } + } + + // A formatted conversion from an expression to a string + #[deriving(Eq)] + pub struct Conv { + param: Option<uint>, + flags: ~[Flag], + width: Count, + precision: Count, + ty: Ty + } + + // A fragment of the output sequence + #[deriving(Eq)] + pub enum Piece { PieceString(~str), PieceConv(Conv), } + + pub type ErrorFn = @fn(&str) -> !; + + pub fn parse_fmt_string(s: &str, err: ErrorFn) -> ~[Piece] { + fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) { + if to > from { + ps.push(PieceString(s.slice(from, to).to_owned())); + } + } + + let lim = s.len(); + let mut h = 0; + let mut i = 0; + let mut pieces = ~[]; + + while i < lim { + if s[i] == '%' as u8 { + i += 1; + + if i >= lim { + err("unterminated conversion at end of string"); + } else if s[i] == '%' as u8 { + push_slice(&mut pieces, s, h, i); + i += 1; + } else { + push_slice(&mut pieces, s, h, i - 1); + let Parsed {val, next} = parse_conversion(s, i, lim, err); + pieces.push(val); + i = next; + } + + h = i; + } else { + i += str::utf8_char_width(s[i]); + } + } + + push_slice(&mut pieces, s, h, i); + pieces + } + + pub fn peek_num(s: &str, i: uint, lim: uint) -> Option<Parsed<uint>> { + let mut i = i; + let mut accum = 0; + let mut found = false; + + while i < lim { + match char::to_digit(s[i] as char, 10) { + Some(x) => { + found = true; + accum *= 10; + accum += x; + i += 1; + } + None => break + } + } + + if found { + Some(Parsed::new(accum, i)) + } else { + None + } + } + + pub fn parse_conversion(s: &str, i: uint, lim: uint, err: ErrorFn) -> + Parsed<Piece> { + let param = parse_parameter(s, i, lim); + // avoid copying ~[Flag] by destructuring + let Parsed {val: flags_val, next: flags_next} = parse_flags(s, + param.next, lim); + let width = parse_count(s, flags_next, lim); + let prec = parse_precision(s, width.next, lim); + let ty = parse_type(s, prec.next, lim, err); + + Parsed::new(PieceConv(Conv { + param: param.val, + flags: flags_val, + width: width.val, + precision: prec.val, + ty: ty.val}), ty.next) + } + + pub fn parse_parameter(s: &str, i: uint, lim: uint) -> + Parsed<Option<uint>> { + if i >= lim { return Parsed::new(None, i); } + + match peek_num(s, i, lim) { + Some(num) if num.next < lim && s[num.next] == '$' as u8 => + Parsed::new(Some(num.val), num.next + 1), + _ => Parsed::new(None, i) + } + } + + pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> { + let mut i = i; + let mut flags = ~[]; + + while i < lim { + let f = match s[i] as char { + '-' => FlagLeftJustify, + '0' => FlagLeftZeroPad, + ' ' => FlagSpaceForSign, + '+' => FlagSignAlways, + '#' => FlagAlternate, + _ => break + }; + + flags.push(f); + i += 1; + } + + Parsed::new(flags, i) + } + + pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed<Count> { + if i >= lim { + Parsed::new(CountImplied, i) + } else if s[i] == '*' as u8 { + let param = parse_parameter(s, i + 1, lim); + let j = param.next; + + match param.val { + None => Parsed::new(CountIsNextParam, j), + Some(n) => Parsed::new(CountIsParam(n), j) + } + } else { + match peek_num(s, i, lim) { + None => Parsed::new(CountImplied, i), + Some(num) => Parsed::new(CountIs(num.val), num.next) + } + } + } + + pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed<Count> { + if i < lim && s[i] == '.' as u8 { + let count = parse_count(s, i + 1, lim); + + // If there were no digits specified, i.e. the precision + // was ".", then the precision is 0 + match count.val { + CountImplied => Parsed::new(CountIs(0), count.next), + _ => count + } + } else { + Parsed::new(CountImplied, i) + } + } + + pub fn parse_type(s: &str, i: uint, lim: uint, err: ErrorFn) -> + Parsed<Ty> { + if i >= lim { err("missing type in conversion"); } + + // FIXME (#2249): Do we really want two signed types here? + // How important is it to be printf compatible? + let t = match s[i] as char { + 'b' => TyBool, + 's' => TyStr, + 'c' => TyChar, + 'd' | 'i' => TyInt(Signed), + 'u' => TyInt(Unsigned), + 'x' => TyHex(CaseLower), + 'X' => TyHex(CaseUpper), + 't' => TyBits, + 'o' => TyOctal, + 'f' => TyFloat, + '?' => TyPoly, + _ => err(~"unknown type in conversion: " + s.substr(i, 1)) + }; + + Parsed::new(t, i + 1) + } + + #[cfg(test)] + fn die(s: &str) -> ! { fail!(s.to_owned()) } + + #[test] + fn test_parse_count() { + fn test(s: &str, count: Count, next: uint) -> bool { + parse_count(s, 0, s.len()) == Parsed::new(count, next) + } + + assert!(test("", CountImplied, 0)); + assert!(test("*", CountIsNextParam, 1)); + assert!(test("*1", CountIsNextParam, 1)); + assert!(test("*1$", CountIsParam(1), 3)); + assert!(test("123", CountIs(123), 3)); + } + + #[test] + fn test_parse_flags() { + fn pack(fs: &[Flag]) -> uint { + fs.foldl(0, |&p, &f| p | (1 << f as uint)) + } + + fn test(s: &str, flags: &[Flag], next: uint) { + let f = parse_flags(s, 0, s.len()); + assert_eq!(pack(f.val), pack(flags)); + assert_eq!(f.next, next); + } + + test("", [], 0); + test("!#-+ 0", [], 0); + test("#-+", [FlagAlternate, FlagLeftJustify, FlagSignAlways], 3); + test(" 0", [FlagSpaceForSign, FlagLeftZeroPad], 2); + } + + #[test] + fn test_parse_fmt_string() { + assert!(parse_fmt_string("foo %s bar", die) == ~[ + PieceString(~"foo "), + PieceConv(Conv { + param: None, + flags: ~[], + width: CountImplied, + precision: CountImplied, + ty: TyStr, + }), + PieceString(~" bar")]); + + assert!(parse_fmt_string("%s", die) == ~[ + PieceConv(Conv { + param: None, + flags: ~[], + width: CountImplied, + precision: CountImplied, + ty: TyStr, + })]); + + assert!(parse_fmt_string("%%%%", die) == ~[ + PieceString(~"%"), PieceString(~"%")]); + } + + #[test] + fn test_parse_parameter() { + fn test(s: &str, param: Option<uint>, next: uint) -> bool { + parse_parameter(s, 0, s.len()) == Parsed::new(param, next) + } + + assert!(test("", None, 0)); + assert!(test("foo", None, 0)); + assert!(test("123", None, 0)); + assert!(test("123$", Some(123), 4)); + } + + #[test] + fn test_parse_precision() { + fn test(s: &str, count: Count, next: uint) -> bool { + parse_precision(s, 0, s.len()) == Parsed::new(count, next) + } + + assert!(test("", CountImplied, 0)); + assert!(test(".", CountIs(0), 1)); + assert!(test(".*", CountIsNextParam, 2)); + assert!(test(".*1", CountIsNextParam, 2)); + assert!(test(".*1$", CountIsParam(1), 4)); + assert!(test(".123", CountIs(123), 4)); + } + + #[test] + fn test_parse_type() { + fn test(s: &str, ty: Ty) -> bool { + parse_type(s, 0, s.len(), die) == Parsed::new(ty, 1) + } + + assert!(test("b", TyBool)); + assert!(test("c", TyChar)); + assert!(test("d", TyInt(Signed))); + assert!(test("f", TyFloat)); + assert!(test("i", TyInt(Signed))); + assert!(test("o", TyOctal)); + assert!(test("s", TyStr)); + assert!(test("t", TyBits)); + assert!(test("x", TyHex(CaseLower))); + assert!(test("X", TyHex(CaseUpper))); + assert!(test("?", TyPoly)); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_parse_type_missing() { + parse_type("", 0, 0, die); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_parse_type_unknown() { + parse_type("!", 0, 1, die); + } + + #[test] + fn test_peek_num() { + let s1 = ""; + assert!(peek_num(s1, 0, s1.len()).is_none()); + + let s2 = "foo"; + assert!(peek_num(s2, 0, s2.len()).is_none()); + + let s3 = "123"; + assert_eq!(peek_num(s3, 0, s3.len()), Some(Parsed::new(123, 3))); + + let s4 = "123foo"; + assert_eq!(peek_num(s4, 0, s4.len()), Some(Parsed::new(123, 3))); + } +} + +// Functions used by the fmt extension at runtime. For now there are a lot of +// decisions made a runtime. If it proves worthwhile then some of these +// conditions can be evaluated at compile-time. For now though it's cleaner to +// implement it this way, I think. +#[doc(hidden)] +pub mod rt { + use float; + use str; + use sys; + use int; + use uint; + use vec; + use option::{Some, None, Option}; + + pub static flag_none : u32 = 0u32; + pub static flag_left_justify : u32 = 0b00000000000001u32; + pub static flag_left_zero_pad : u32 = 0b00000000000010u32; + pub static flag_space_for_sign : u32 = 0b00000000000100u32; + pub static flag_sign_always : u32 = 0b00000000001000u32; + pub static flag_alternate : u32 = 0b00000000010000u32; + + pub enum Count { CountIs(uint), CountImplied, } + + pub enum Ty { TyDefault, TyBits, TyHexUpper, TyHexLower, TyOctal, } + + pub struct Conv { + flags: u32, + width: Count, + precision: Count, + ty: Ty, + } + + pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) { + let radix = 10; + let prec = get_int_precision(cv); + let s : ~str = uint_to_str_prec(int::abs(i) as uint, radix, prec); + + let head = if i >= 0 { + if have_flag(cv.flags, flag_sign_always) { + Some('+') + } else if have_flag(cv.flags, flag_space_for_sign) { + Some(' ') + } else { + None + } + } else { Some('-') }; + pad(cv, s, head, PadSigned, buf); + } + pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) { + let prec = get_int_precision(cv); + let rs = + match cv.ty { + TyDefault => uint_to_str_prec(u, 10, prec), + TyHexLower => uint_to_str_prec(u, 16, prec), + + // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use + // to_ascii_consume and to_str_consume to not do a unnecessary copy. + TyHexUpper => { + let s = uint_to_str_prec(u, 16, prec); + s.to_ascii().to_upper().to_str_ascii() + } + TyBits => uint_to_str_prec(u, 2, prec), + TyOctal => uint_to_str_prec(u, 8, prec) + }; + pad(cv, rs, None, PadUnsigned, buf); + } + pub fn conv_bool(cv: Conv, b: bool, buf: &mut ~str) { + let s = if b { "true" } else { "false" }; + // run the boolean conversion through the string conversion logic, + // giving it the same rules for precision, etc. + conv_str(cv, s, buf); + } + pub fn conv_char(cv: Conv, c: char, buf: &mut ~str) { + pad(cv, "", Some(c), PadNozero, buf); + } + pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) { + // For strings, precision is the maximum characters + // displayed + let unpadded = match cv.precision { + CountImplied => s, + CountIs(max) => if (max as uint) < str::char_len(s) { + str::slice(s, 0, max as uint) + } else { + s + } + }; + pad(cv, unpadded, None, PadNozero, buf); + } + pub fn conv_float(cv: Conv, f: float, buf: &mut ~str) { + let (to_str, digits) = match cv.precision { + CountIs(c) => (float::to_str_exact, c as uint), + CountImplied => (float::to_str_digits, 6u) + }; + let s = to_str(f, digits); + let head = if 0.0 <= f { + if have_flag(cv.flags, flag_sign_always) { + Some('+') + } else if have_flag(cv.flags, flag_space_for_sign) { + Some(' ') + } else { + None + } + } else { None }; + pad(cv, s, head, PadFloat, buf); + } + pub fn conv_poly<T>(cv: Conv, v: &T, buf: &mut ~str) { + let s = sys::log_str(v); + conv_str(cv, s, buf); + } + + // Convert a uint to string with a minimum number of digits. If precision + // is 0 and num is 0 then the result is the empty string. Could move this + // to uint: but it doesn't seem all that useful. + pub fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> ~str { + return if prec == 0u && num == 0u { + ~"" + } else { + let s = uint::to_str_radix(num, radix); + let len = str::char_len(s); + if len < prec { + let diff = prec - len; + let pad = str::from_chars(vec::from_elem(diff, '0')); + pad + s + } else { s } + }; + } + pub fn get_int_precision(cv: Conv) -> uint { + return match cv.precision { + CountIs(c) => c as uint, + CountImplied => 1u + }; + } + + #[deriving(Eq)] + pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat } + + pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode, + buf: &mut ~str) { + let headsize = match head { Some(_) => 1, _ => 0 }; + let uwidth : uint = match cv.width { + CountImplied => { + for head.each |&c| { + buf.push_char(c); + } + return buf.push_str(s); + } + CountIs(width) => { width as uint } + }; + let strlen = str::char_len(s) + headsize; + if uwidth <= strlen { + for head.each |&c| { + buf.push_char(c); + } + return buf.push_str(s); + } + let mut padchar = ' '; + let diff = uwidth - strlen; + if have_flag(cv.flags, flag_left_justify) { + for head.each |&c| { + buf.push_char(c); + } + buf.push_str(s); + for diff.times { + buf.push_char(padchar); + } + return; + } + let (might_zero_pad, signed) = match mode { + PadNozero => (false, true), + PadSigned => (true, true), + PadFloat => (true, true), + PadUnsigned => (true, false) + }; + fn have_precision(cv: Conv) -> bool { + return match cv.precision { CountImplied => false, _ => true }; + } + let zero_padding = { + if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) && + (!have_precision(cv) || mode == PadFloat) { + padchar = '0'; + true + } else { + false + } + }; + let padstr = str::from_chars(vec::from_elem(diff, padchar)); + // This is completely heinous. If we have a signed value then + // potentially rip apart the intermediate result and insert some + // zeros. It may make sense to convert zero padding to a precision + // instead. + + if signed && zero_padding { + for head.each |&head| { + if head == '+' || head == '-' || head == ' ' { + buf.push_char(head); + buf.push_str(padstr); + buf.push_str(s); + return; + } + } + } + buf.push_str(padstr); + for head.each |&c| { + buf.push_char(c); + } + buf.push_str(s); + } + #[inline(always)] + pub fn have_flag(flags: u32, f: u32) -> bool { + flags & f != 0 + } +} + +// Bulk of the tests are in src/test/run-pass/syntax-extension-fmt.rs +#[cfg(test)] +mod test { + #[test] + fn fmt_slice() { + let s = "abc"; + let _s = fmt!("%s", s); + } +} diff --git a/src/libstd/unstable/finally.rs b/src/libstd/unstable/finally.rs new file mode 100644 index 00000000000..5001fb421cd --- /dev/null +++ b/src/libstd/unstable/finally.rs @@ -0,0 +1,139 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +The Finally trait provides a method, `finally` on +stack closures that emulates Java-style try/finally blocks. + +# Example + +~~~ +do || { + ... +}.finally { + alway_run_this(); +} +~~~ +*/ + +use ops::Drop; + +#[cfg(test)] use task::{failing, spawn}; + +pub trait Finally<T> { + fn finally(&self, dtor: &fn()) -> T; +} + +impl<'self,T> Finally<T> for &'self fn() -> T { + fn finally(&self, dtor: &fn()) -> T { + let _d = Finallyalizer { + dtor: dtor + }; + + (*self)() + } +} + +impl<T> Finally<T> for ~fn() -> T { + fn finally(&self, dtor: &fn()) -> T { + let _d = Finallyalizer { + dtor: dtor + }; + + (*self)() + } +} + +impl<T> Finally<T> for @fn() -> T { + fn finally(&self, dtor: &fn()) -> T { + let _d = Finallyalizer { + dtor: dtor + }; + + (*self)() + } +} + +struct Finallyalizer<'self> { + dtor: &'self fn() +} + +#[unsafe_destructor] +impl<'self> Drop for Finallyalizer<'self> { + fn finalize(&self) { + (self.dtor)(); + } +} + +#[test] +fn test_success() { + let mut i = 0; + do (|| { + i = 10; + }).finally { + assert!(!failing()); + assert_eq!(i, 10); + i = 20; + } + assert_eq!(i, 20); +} + +#[test] +#[ignore(cfg(windows))] +#[should_fail] +fn test_fail() { + let mut i = 0; + do (|| { + i = 10; + fail!(); + }).finally { + assert!(failing()); + assert_eq!(i, 10); + } +} + +#[test] +fn test_retval() { + let closure: &fn() -> int = || 10; + let i = do closure.finally { }; + assert_eq!(i, 10); +} + +#[test] +fn test_compact() { + // FIXME #4727: Should be able to use a fn item instead + // of a closure for do_some_fallible_work, + // but it's a type error. + let do_some_fallible_work: &fn() = || { }; + fn but_always_run_this_function() { } + do_some_fallible_work.finally( + but_always_run_this_function); +} + +#[test] +fn test_owned() { + fn spawn_with_finalizer(f: ~fn()) { + do spawn { do f.finally { } } + } + let owned: ~fn() = || { }; + spawn_with_finalizer(owned); +} + +#[test] +fn test_managed() { + let i = @mut 10; + let managed: @fn() -> int = || { + let r = *i; + *i += 10; + r + }; + assert_eq!(do managed.finally {}, 10); + assert_eq!(*i, 20); +} \ No newline at end of file diff --git a/src/libstd/unstable/global.rs b/src/libstd/unstable/global.rs new file mode 100644 index 00000000000..96549a83a8c --- /dev/null +++ b/src/libstd/unstable/global.rs @@ -0,0 +1,281 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +Global data + +An interface for creating and retrieving values with global +(per-runtime) scope. + +Global values are stored in a map and protected by a single global +mutex. Operations are provided for accessing and cloning the value +under the mutex. + +Because all globals go through a single mutex, they should be used +sparingly. The interface is intended to be used with clonable, +atomically reference counted synchronization types, like ARCs, in +which case the value should be cached locally whenever possible to +avoid hitting the mutex. +*/ + +use cast::{transmute}; +use clone::Clone; +use kinds::Owned; +use libc::{c_void}; +use option::{Option, Some, None}; +use ops::Drop; +use unstable::sync::{Exclusive, exclusive}; +use unstable::at_exit::at_exit; +use unstable::intrinsics::atomic_cxchg; +use hashmap::HashMap; +use sys::Closure; + +#[cfg(test)] use unstable::sync::{UnsafeAtomicRcBox}; +#[cfg(test)] use task::spawn; +#[cfg(test)] use uint; + +pub type GlobalDataKey<'self,T> = &'self fn(v: T); + +pub unsafe fn global_data_clone_create<T:Owned + Clone>( + key: GlobalDataKey<T>, create: &fn() -> ~T) -> T { + /*! + * Clone a global value or, if it has not been created, + * first construct the value then return a clone. + * + * # Safety note + * + * Both the clone operation and the constructor are + * called while the global lock is held. Recursive + * use of the global interface in either of these + * operations will result in deadlock. + */ + global_data_clone_create_(key_ptr(key), create) +} + +unsafe fn global_data_clone_create_<T:Owned + Clone>( + key: uint, create: &fn() -> ~T) -> T { + + let mut clone_value: Option<T> = None; + do global_data_modify_(key) |value: Option<~T>| { + match value { + None => { + let value = create(); + clone_value = Some((*value).clone()); + Some(value) + } + Some(value) => { + clone_value = Some((*value).clone()); + Some(value) + } + } + } + return clone_value.unwrap(); +} + +unsafe fn global_data_modify<T:Owned>( + key: GlobalDataKey<T>, op: &fn(Option<~T>) -> Option<~T>) { + + global_data_modify_(key_ptr(key), op) +} + +unsafe fn global_data_modify_<T:Owned>( + key: uint, op: &fn(Option<~T>) -> Option<~T>) { + + let mut old_dtor = None; + do get_global_state().with |gs| { + let (maybe_new_value, maybe_dtor) = match gs.map.pop(&key) { + Some((ptr, dtor)) => { + let value: ~T = transmute(ptr); + (op(Some(value)), Some(dtor)) + } + None => { + (op(None), None) + } + }; + match maybe_new_value { + Some(value) => { + let data: *c_void = transmute(value); + let dtor: ~fn() = match maybe_dtor { + Some(dtor) => dtor, + None => { + let dtor: ~fn() = || unsafe { + let _destroy_value: ~T = transmute(data); + }; + dtor + } + }; + let value = (data, dtor); + gs.map.insert(key, value); + } + None => { + match maybe_dtor { + Some(dtor) => old_dtor = Some(dtor), + None => () + } + } + } + } +} + +pub unsafe fn global_data_clone<T:Owned + Clone>( + key: GlobalDataKey<T>) -> Option<T> { + let mut maybe_clone: Option<T> = None; + do global_data_modify(key) |current| { + match ¤t { + &Some(~ref value) => { + maybe_clone = Some(value.clone()); + } + &None => () + } + current + } + return maybe_clone; +} + +// GlobalState is a map from keys to unique pointers and a +// destructor. Keys are pointers derived from the type of the +// global value. There is a single GlobalState instance per runtime. +struct GlobalState { + map: HashMap<uint, (*c_void, ~fn())> +} + +impl Drop for GlobalState { + fn finalize(&self) { + for self.map.each_value |v| { + match v { + &(_, ref dtor) => (*dtor)() + } + } + } +} + +fn get_global_state() -> Exclusive<GlobalState> { + + static POISON: int = -1; + + // FIXME #4728: Doing atomic_cxchg to initialize the global state + // lazily, which wouldn't be necessary with a runtime written + // in Rust + let global_ptr = unsafe { rust_get_global_data_ptr() }; + + if unsafe { *global_ptr } == 0 { + // Global state doesn't exist yet, probably + + // The global state object + let state = GlobalState { + map: HashMap::new() + }; + + // It's under a reference-counted mutex + let state = ~exclusive(state); + + // Convert it to an integer + let state_i: int = unsafe { + let state_ptr: &Exclusive<GlobalState> = state; + transmute(state_ptr) + }; + + // Swap our structure into the global pointer + let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) }; + + // Sanity check that we're not trying to reinitialize after shutdown + assert!(prev_i != POISON); + + if prev_i == 0 { + // Successfully installed the global pointer + + // Take a handle to return + let clone = (*state).clone(); + + // Install a runtime exit function to destroy the global object + do at_exit { + // Poison the global pointer + let prev_i = unsafe { + atomic_cxchg(&mut *global_ptr, state_i, POISON) + }; + assert_eq!(prev_i, state_i); + + // Capture the global state object in the at_exit closure + // so that it is destroyed at the right time + let _capture_global_state = &state; + }; + return clone; + } else { + // Somebody else initialized the globals first + let state: &Exclusive<GlobalState> = unsafe { transmute(prev_i) }; + return state.clone(); + } + } else { + let state: &Exclusive<GlobalState> = unsafe { + transmute(*global_ptr) + }; + return state.clone(); + } +} + +fn key_ptr<T:Owned>(key: GlobalDataKey<T>) -> uint { + unsafe { + let closure: Closure = transmute(key); + return transmute(closure.code); + } +} + +extern { + fn rust_get_global_data_ptr() -> *mut int; +} + +#[test] +fn test_clone_rc() { + fn key(_v: UnsafeAtomicRcBox<int>) { } + + for uint::range(0, 100) |_| { + do spawn { + unsafe { + let val = do global_data_clone_create(key) { + ~UnsafeAtomicRcBox::new(10) + }; + + assert!(val.get() == &10); + } + } + } +} + +#[test] +fn test_modify() { + fn key(_v: UnsafeAtomicRcBox<int>) { } + + unsafe { + do global_data_modify(key) |v| { + match v { + None => { Some(~UnsafeAtomicRcBox::new(10)) } + _ => fail!() + } + } + + do global_data_modify(key) |v| { + match v { + Some(sms) => { + let v = sms.get(); + assert!(*v == 10); + None + }, + _ => fail!() + } + } + + do global_data_modify(key) |v| { + match v { + None => { Some(~UnsafeAtomicRcBox::new(10)) } + _ => fail!() + } + } + } +} diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs new file mode 100644 index 00000000000..d476822819e --- /dev/null +++ b/src/libstd/unstable/intrinsics.rs @@ -0,0 +1,211 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! rustc compiler intrinsics. + +The corresponding definitions are in librustc/middle/trans/foreign.rs. + +# Atomics + +The atomic intrinsics provide common atomic operations on machine +words, with multiple possible memory orderings. They obey the same +semantics as C++0x. See the LLVM documentation on [[atomics]]. + +[atomics]: http://llvm.org/docs/Atomics.html + +A quick refresher on memory ordering: + +* Acquire - a barrier for aquiring a lock. Subsequent reads and writes + take place after the barrier. +* Release - a barrier for releasing a lock. Preceding reads and writes + take place before the barrier. +* Sequentially consistent - sequentially consistent operations are + guaranteed to happen in order. This is the standard mode for working + with atomic types and is equivalent to Java's `volatile`. + +*/ + +#[abi = "rust-intrinsic"] +pub extern "rust-intrinsic" { + + /// Atomic compare and exchange, sequentially consistent. + pub fn atomic_cxchg(dst: &mut int, old: int, src: int) -> int; + /// Atomic compare and exchange, acquire ordering. + pub fn atomic_cxchg_acq(dst: &mut int, old: int, src: int) -> int; + /// Atomic compare and exchange, release ordering. + pub fn atomic_cxchg_rel(dst: &mut int, old: int, src: int) -> int; + + /// Atomic load, sequentially consistent. + pub fn atomic_load(src: &int) -> int; + /// Atomic load, acquire ordering. + pub fn atomic_load_acq(src: &int) -> int; + + /// Atomic store, sequentially consistent. + pub fn atomic_store(dst: &mut int, val: int); + /// Atomic store, release ordering. + pub fn atomic_store_rel(dst: &mut int, val: int); + + /// Atomic exchange, sequentially consistent. + pub fn atomic_xchg(dst: &mut int, src: int) -> int; + /// Atomic exchange, acquire ordering. + pub fn atomic_xchg_acq(dst: &mut int, src: int) -> int; + /// Atomic exchange, release ordering. + pub fn atomic_xchg_rel(dst: &mut int, src: int) -> int; + + /// Atomic addition, sequentially consistent. + pub fn atomic_xadd(dst: &mut int, src: int) -> int; + /// Atomic addition, acquire ordering. + pub fn atomic_xadd_acq(dst: &mut int, src: int) -> int; + /// Atomic addition, release ordering. + pub fn atomic_xadd_rel(dst: &mut int, src: int) -> int; + + /// Atomic subtraction, sequentially consistent. + pub fn atomic_xsub(dst: &mut int, src: int) -> int; + /// Atomic subtraction, acquire ordering. + pub fn atomic_xsub_acq(dst: &mut int, src: int) -> int; + /// Atomic subtraction, release ordering. + pub fn atomic_xsub_rel(dst: &mut int, src: int) -> int; + + /// The size of a type in bytes. + /// + /// This is the exact number of bytes in memory taken up by a + /// value of the given type. In other words, a memset of this size + /// would *exactly* overwrite a value. When laid out in vectors + /// and structures there may be additional padding between + /// elements. + pub fn size_of<T>() -> uint; + + /// Move a value to a memory location containing a value. + /// + /// Drop glue is run on the destination, which must contain a + /// valid Rust value. + pub fn move_val<T>(dst: &mut T, src: T); + + /// Move a value to an uninitialized memory location. + /// + /// Drop glue is not run on the destination. + pub fn move_val_init<T>(dst: &mut T, src: T); + + pub fn min_align_of<T>() -> uint; + pub fn pref_align_of<T>() -> uint; + + /// Get a static pointer to a type descriptor. + pub fn get_tydesc<T>() -> *(); + + /// Create a value initialized to zero. + /// + /// `init` is unsafe because it returns a zeroed-out datum, + /// which is unsafe unless T is POD. We don't have a POD + /// kind yet. (See #4074). + pub unsafe fn init<T>() -> T; + + /// Create an uninitialized value. + pub unsafe fn uninit<T>() -> T; + + /// Move a value out of scope without running drop glue. + /// + /// `forget` is unsafe because the caller is responsible for + /// ensuring the argument is deallocated already. + pub unsafe fn forget<T>(_: T) -> (); + pub fn transmute<T,U>(e: T) -> U; + + /// Returns `true` if a type requires drop glue. + pub fn needs_drop<T>() -> bool; + + // XXX: intrinsic uses legacy modes and has reference to TyDesc + // and TyVisitor which are in librustc + //fn visit_tydesc(++td: *TyDesc, &&tv: TyVisitor) -> (); + + pub fn frame_address(f: &once fn(*u8)); + + /// Get the address of the `__morestack` stack growth function. + pub fn morestack_addr() -> *(); + + /// Equivalent to the `llvm.memmove.p0i8.0i8.i32` intrinsic. + pub fn memmove32(dst: *mut u8, src: *u8, size: u32); + /// Equivalent to the `llvm.memmove.p0i8.0i8.i64` intrinsic. + pub fn memmove64(dst: *mut u8, src: *u8, size: u64); + + pub fn sqrtf32(x: f32) -> f32; + pub fn sqrtf64(x: f64) -> f64; + + pub fn powif32(a: f32, x: i32) -> f32; + pub fn powif64(a: f64, x: i32) -> f64; + + // the following kill the stack canary without + // `fixed_stack_segment`. This possibly only affects the f64 + // variants, but it's hard to be sure since it seems to only + // occur with fairly specific arguments. + #[fixed_stack_segment] + pub fn sinf32(x: f32) -> f32; + #[fixed_stack_segment] + pub fn sinf64(x: f64) -> f64; + + #[fixed_stack_segment] + pub fn cosf32(x: f32) -> f32; + #[fixed_stack_segment] + pub fn cosf64(x: f64) -> f64; + + #[fixed_stack_segment] + pub fn powf32(a: f32, x: f32) -> f32; + #[fixed_stack_segment] + pub fn powf64(a: f64, x: f64) -> f64; + + #[fixed_stack_segment] + pub fn expf32(x: f32) -> f32; + #[fixed_stack_segment] + pub fn expf64(x: f64) -> f64; + + pub fn exp2f32(x: f32) -> f32; + pub fn exp2f64(x: f64) -> f64; + + pub fn logf32(x: f32) -> f32; + pub fn logf64(x: f64) -> f64; + + pub fn log10f32(x: f32) -> f32; + pub fn log10f64(x: f64) -> f64; + + pub fn log2f32(x: f32) -> f32; + pub fn log2f64(x: f64) -> f64; + + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; + pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + + pub fn fabsf32(x: f32) -> f32; + pub fn fabsf64(x: f64) -> f64; + + pub fn floorf32(x: f32) -> f32; + pub fn floorf64(x: f64) -> f64; + + pub fn ceilf32(x: f32) -> f32; + pub fn ceilf64(x: f64) -> f64; + + pub fn truncf32(x: f32) -> f32; + pub fn truncf64(x: f64) -> f64; + + pub fn ctpop8(x: i8) -> i8; + pub fn ctpop16(x: i16) -> i16; + pub fn ctpop32(x: i32) -> i32; + pub fn ctpop64(x: i64) -> i64; + + pub fn ctlz8(x: i8) -> i8; + pub fn ctlz16(x: i16) -> i16; + pub fn ctlz32(x: i32) -> i32; + pub fn ctlz64(x: i64) -> i64; + + pub fn cttz8(x: i8) -> i8; + pub fn cttz16(x: i16) -> i16; + pub fn cttz32(x: i32) -> i32; + pub fn cttz64(x: i64) -> i64; + + pub fn bswap16(x: i16) -> i16; + pub fn bswap32(x: i32) -> i32; + pub fn bswap64(x: i64) -> i64; +} diff --git a/src/libstd/unstable/lang.rs b/src/libstd/unstable/lang.rs new file mode 100644 index 00000000000..350b18d4541 --- /dev/null +++ b/src/libstd/unstable/lang.rs @@ -0,0 +1,420 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Runtime calls emitted by the compiler. + +use uint; +use cast::transmute; +use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO}; +use managed::raw::BoxRepr; +use str; +use sys; +use rt::{context, OldTaskContext}; +use rt::task::Task; +use rt::local::Local; +use option::{Option, Some, None}; +use io; +use rt::global_heap; + +#[allow(non_camel_case_types)] +pub type rust_task = c_void; + +pub static FROZEN_BIT: uint = 1 << (uint::bits - 1); +pub static MUT_BIT: uint = 1 << (uint::bits - 2); +static ALL_BITS: uint = FROZEN_BIT | MUT_BIT; + +pub mod rustrt { + use unstable::lang::rust_task; + use libc::{c_void, c_char, uintptr_t}; + + pub extern { + #[rust_stack] + unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char; + + #[rust_stack] + unsafe fn rust_upcall_free(ptr: *c_char); + + #[fast_ffi] + unsafe fn rust_upcall_malloc_noswitch(td: *c_char, + size: uintptr_t) + -> *c_char; + + #[fast_ffi] + unsafe fn rust_upcall_free_noswitch(ptr: *c_char); + + #[rust_stack] + fn rust_take_task_borrow_list(task: *rust_task) -> *c_void; + + #[rust_stack] + fn rust_set_task_borrow_list(task: *rust_task, map: *c_void); + + #[rust_stack] + fn rust_try_get_task() -> *rust_task; + + fn rust_dbg_breakpoint(); + } +} + +#[lang="fail_"] +pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! { + sys::begin_unwind_(expr, file, line); +} + +#[lang="fail_bounds_check"] +pub fn fail_bounds_check(file: *c_char, line: size_t, + index: size_t, len: size_t) { + let msg = fmt!("index out of bounds: the len is %d but the index is %d", + len as int, index as int); + do str::as_buf(msg) |p, _len| { + fail_(p as *c_char, file, line); + } +} + +#[deriving(Eq)] +struct BorrowRecord { + box: *mut BoxRepr, + file: *c_char, + line: size_t +} + +fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> { + unsafe { + let cur_task: *rust_task = rustrt::rust_try_get_task(); + if cur_task.is_not_null() { + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { + None + } else { + let v: ~[BorrowRecord] = transmute(ptr); + Some(v) + } + } else { + None + } + } +} + +fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { + unsafe { + let cur_task: *rust_task = rustrt::rust_try_get_task(); + if cur_task.is_not_null() { + let mut borrow_list: ~[BorrowRecord] = { + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { ~[] } else { transmute(ptr) } + }; + borrow_list = f(borrow_list); + rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list)); + } + } +} + +pub unsafe fn clear_task_borrow_list() { + // pub because it is used by the box annihilator. + let _ = try_take_task_borrow_list(); +} + +unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { + debug_borrow("fail_borrowed: ", box, 0, 0, file, line); + + match try_take_task_borrow_list() { + None => { // not recording borrows + let msg = "borrowed"; + do str::as_buf(msg) |msg_p, _| { + fail_(msg_p as *c_char, file, line); + } + } + Some(borrow_list) => { // recording borrows + let mut msg = ~"borrowed"; + let mut sep = " at "; + for borrow_list.each_reverse |entry| { + if entry.box == box { + str::push_str(&mut msg, sep); + let filename = str::raw::from_c_str(entry.file); + str::push_str(&mut msg, filename); + str::push_str(&mut msg, fmt!(":%u", entry.line as uint)); + sep = " and at "; + } + } + do str::as_buf(msg) |msg_p, _| { + fail_(msg_p as *c_char, file, line) + } + } + } +} + +// FIXME #4942: Make these signatures agree with exchange_alloc's signatures +#[lang="exchange_malloc"] +#[inline(always)] +pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { + transmute(global_heap::malloc(transmute(td), transmute(size))) +} + +/// Because this code is so perf. sensitive, use a static constant so that +/// debug printouts are compiled out most of the time. +static ENABLE_DEBUG: bool = false; + +#[inline] +unsafe fn debug_borrow<T>(tag: &'static str, + p: *const T, + old_bits: uint, + new_bits: uint, + filename: *c_char, + line: size_t) { + //! A useful debugging function that prints a pointer + tag + newline + //! without allocating memory. + + if ENABLE_DEBUG && ::rt::env::get().debug_borrow { + debug_borrow_slow(tag, p, old_bits, new_bits, filename, line); + } + + unsafe fn debug_borrow_slow<T>(tag: &'static str, + p: *const T, + old_bits: uint, + new_bits: uint, + filename: *c_char, + line: size_t) { + let dbg = STDERR_FILENO as io::fd_t; + dbg.write_str(tag); + dbg.write_hex(p as uint); + dbg.write_str(" "); + dbg.write_hex(old_bits); + dbg.write_str(" "); + dbg.write_hex(new_bits); + dbg.write_str(" "); + dbg.write_cstr(filename); + dbg.write_str(":"); + dbg.write_hex(line as uint); + dbg.write_str("\n"); + } +} + +trait DebugPrints { + fn write_hex(&self, val: uint); + unsafe fn write_cstr(&self, str: *c_char); +} + +impl DebugPrints for io::fd_t { + fn write_hex(&self, mut i: uint) { + let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f']; + static uint_nibbles: uint = ::uint::bytes << 1; + let mut buffer = [0_u8, ..uint_nibbles+1]; + let mut c = uint_nibbles; + while c > 0 { + c -= 1; + buffer[c] = letters[i & 0xF] as u8; + i >>= 4; + } + self.write(buffer.slice(0, uint_nibbles)); + } + + unsafe fn write_cstr(&self, p: *c_char) { + use libc::strlen; + use vec; + + let len = strlen(p); + let p: *u8 = transmute(p); + do vec::raw::buf_as_slice(p, len as uint) |s| { + self.write(s); + } + } +} + +// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from +// inside a landing pad may corrupt the state of the exception handler. If a +// problem occurs, call exit instead. +#[lang="exchange_free"] +#[inline(always)] +pub unsafe fn exchange_free(ptr: *c_char) { + global_heap::free(transmute(ptr)) +} + +#[lang="malloc"] +pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { + match context() { + OldTaskContext => { + return rustrt::rust_upcall_malloc_noswitch(td, size); + } + _ => { + let mut alloc = ::ptr::null(); + do Local::borrow::<Task> |task| { + alloc = task.heap.alloc(td as *c_void, size as uint) as *c_char; + } + return alloc; + } + } +} + +// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from +// inside a landing pad may corrupt the state of the exception handler. If a +// problem occurs, call exit instead. +#[lang="free"] +pub unsafe fn local_free(ptr: *c_char) { + match context() { + OldTaskContext => { + rustrt::rust_upcall_free_noswitch(ptr); + } + _ => { + do Local::borrow::<Task> |task| { + task.heap.free(ptr as *c_void); + } + } + } +} + +#[lang="borrow_as_imm"] +#[inline(always)] +pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { + let a: *mut BoxRepr = transmute(a); + let old_ref_count = (*a).header.ref_count; + let new_ref_count = old_ref_count | FROZEN_BIT; + + debug_borrow("borrow_as_imm:", a, old_ref_count, new_ref_count, file, line); + + if (old_ref_count & MUT_BIT) != 0 { + fail_borrowed(a, file, line); + } + + (*a).header.ref_count = new_ref_count; + + old_ref_count +} + +#[lang="borrow_as_mut"] +#[inline(always)] +pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { + let a: *mut BoxRepr = transmute(a); + let old_ref_count = (*a).header.ref_count; + let new_ref_count = old_ref_count | MUT_BIT | FROZEN_BIT; + + debug_borrow("borrow_as_mut:", a, old_ref_count, new_ref_count, file, line); + + if (old_ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { + fail_borrowed(a, file, line); + } + + (*a).header.ref_count = new_ref_count; + + old_ref_count +} + + +#[lang="record_borrow"] +pub unsafe fn record_borrow(a: *u8, old_ref_count: uint, + file: *c_char, line: size_t) { + if (old_ref_count & ALL_BITS) == 0 { + // was not borrowed before + let a: *mut BoxRepr = transmute(a); + debug_borrow("record_borrow:", a, old_ref_count, 0, file, line); + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + borrow_list.push(BorrowRecord {box: a, file: file, line: line}); + borrow_list + } + } +} + +#[lang="unrecord_borrow"] +pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, + file: *c_char, line: size_t) { + if (old_ref_count & ALL_BITS) == 0 { + // was not borrowed before, so we should find the record at + // the end of the list + let a: *mut BoxRepr = transmute(a); + debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line); + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + assert!(!borrow_list.is_empty()); + let br = borrow_list.pop(); + if br.box != a || br.file != file || br.line != line { + let err = fmt!("wrong borrow found, br=%?", br); + do str::as_buf(err) |msg_p, _| { + fail_(msg_p as *c_char, file, line) + } + } + borrow_list + } + } +} + +#[lang="return_to_mut"] +#[inline(always)] +pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint, + file: *c_char, line: size_t) { + // Sometimes the box is null, if it is conditionally frozen. + // See e.g. #4904. + if !a.is_null() { + let a: *mut BoxRepr = transmute(a); + let old_ref_count = (*a).header.ref_count; + let new_ref_count = + (old_ref_count & !ALL_BITS) | (orig_ref_count & ALL_BITS); + + debug_borrow("return_to_mut:", + a, old_ref_count, new_ref_count, file, line); + + (*a).header.ref_count = new_ref_count; + } +} + +#[lang="check_not_borrowed"] +#[inline(always)] +pub unsafe fn check_not_borrowed(a: *u8, + file: *c_char, + line: size_t) { + let a: *mut BoxRepr = transmute(a); + let ref_count = (*a).header.ref_count; + debug_borrow("check_not_borrowed:", a, ref_count, 0, file, line); + if (ref_count & FROZEN_BIT) != 0 { + fail_borrowed(a, file, line); + } +} + +#[lang="strdup_uniq"] +#[inline(always)] +pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str { + str::raw::from_buf_len(ptr, len) +} + +#[lang="start"] +pub fn start(main: *u8, argc: int, argv: **c_char, + crate_map: *u8) -> int { + use rt; + use sys::Closure; + use ptr; + use cast; + use os; + + unsafe { + let use_old_rt = os::getenv("RUST_NEWRT").is_none(); + if use_old_rt { + return rust_start(main as *c_void, argc as c_int, argv, + crate_map as *c_void) as int; + } else { + return do rt::start(argc, argv as **u8, crate_map) { + unsafe { + // `main` is an `fn() -> ()` that doesn't take an environment + // XXX: Could also call this as an `extern "Rust" fn` once they work + let main = Closure { + code: main as *(), + env: ptr::null(), + }; + let mainfn: &fn() = cast::transmute(main); + + mainfn(); + } + }; + } + } + + extern { + fn rust_start(main: *c_void, argc: c_int, argv: **c_char, + crate_map: *c_void) -> c_int; + } +} diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs new file mode 100644 index 00000000000..9681a3f36fc --- /dev/null +++ b/src/libstd/unstable/mod.rs @@ -0,0 +1,77 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +#[doc(hidden)]; + +use libc; +use comm::{GenericChan, GenericPort}; +use prelude::*; +use task; + +pub mod at_exit; +pub mod global; +pub mod finally; +pub mod weak_task; +pub mod intrinsics; +pub mod simd; +pub mod extfmt; +#[cfg(not(test))] +pub mod lang; +pub mod sync; + +/** + +Start a new thread outside of the current runtime context and wait +for it to terminate. + +The executing thread has no access to a task pointer and will be using +a normal large stack. +*/ +pub fn run_in_bare_thread(f: ~fn()) { + let (port, chan) = comm::stream(); + // FIXME #4525: Unfortunate that this creates an extra scheduler but it's + // necessary since rust_raw_thread_join_delete is blocking + do task::spawn_sched(task::SingleThreaded) { + unsafe { + let closure: &fn() = || { + f() + }; + let thread = rust_raw_thread_start(&closure); + rust_raw_thread_join_delete(thread); + chan.send(()); + } + } + port.recv(); +} + +#[test] +fn test_run_in_bare_thread() { + let i = 100; + do run_in_bare_thread { + assert_eq!(i, 100); + } +} + +#[test] +fn test_run_in_bare_thread_exchange() { + // Does the exchange heap work without the runtime? + let i = ~100; + do run_in_bare_thread { + assert!(i == ~100); + } +} + +#[allow(non_camel_case_types)] // runtime type +pub type raw_thread = libc::c_void; + +extern { + fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread; + fn rust_raw_thread_join_delete(thread: *raw_thread); +} diff --git a/src/libstd/unstable/simd.rs b/src/libstd/unstable/simd.rs new file mode 100644 index 00000000000..a05f6e8af5a --- /dev/null +++ b/src/libstd/unstable/simd.rs @@ -0,0 +1,43 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! SIMD vectors + +#[allow(non_camel_case_types)]; + +#[simd] +pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); + +#[simd] +pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); + +#[simd] +pub struct i32x4(i32, i32, i32, i32); + +#[simd] +pub struct i64x2(i64, i64); + +#[simd] +pub struct u8x16(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8); + +#[simd] +pub struct u16x8(u16, u16, u16, u16, u16, u16, u16, u16); + +#[simd] +pub struct u32x4(u32, u32, u32, u32); + +#[simd] +pub struct u64x2(u64, u64); + +#[simd] +pub struct f32x4(f32, f32, f32, f32); + +#[simd] +pub struct f64x2(f64, f64); diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs new file mode 100644 index 00000000000..734368c70c4 --- /dev/null +++ b/src/libstd/unstable/sync.rs @@ -0,0 +1,261 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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 cast; +use libc; +use option::*; +use task; +use task::atomically; +use unstable::finally::Finally; +use unstable::intrinsics; +use ops::Drop; +use clone::Clone; +use kinds::Owned; + +/// An atomically reference counted pointer. +/// +/// Enforces no shared-memory safety. +pub struct UnsafeAtomicRcBox<T> { + data: *mut libc::c_void, +} + +struct AtomicRcBoxData<T> { + count: int, + data: Option<T>, +} + +impl<T: Owned> UnsafeAtomicRcBox<T> { + pub fn new(data: T) -> UnsafeAtomicRcBox<T> { + unsafe { + let data = ~AtomicRcBoxData { count: 1, data: Some(data) }; + let ptr = cast::transmute(data); + return UnsafeAtomicRcBox { data: ptr }; + } + } + + #[inline(always)] + pub unsafe fn get(&self) -> *mut T + { + let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data); + assert!(data.count > 0); + let r: *mut T = data.data.get_mut_ref(); + cast::forget(data); + return r; + } + + #[inline(always)] + pub unsafe fn get_immut(&self) -> *T + { + let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data); + assert!(data.count > 0); + let r: *T = cast::transmute_immut(data.data.get_mut_ref()); + cast::forget(data); + return r; + } +} + +impl<T: Owned> Clone for UnsafeAtomicRcBox<T> { + fn clone(&self) -> UnsafeAtomicRcBox<T> { + unsafe { + let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data); + let new_count = intrinsics::atomic_xadd(&mut data.count, 1) + 1; + assert!(new_count >= 2); + cast::forget(data); + return UnsafeAtomicRcBox { data: self.data }; + } + } +} + +#[unsafe_destructor] +impl<T> Drop for UnsafeAtomicRcBox<T>{ + fn finalize(&self) { + unsafe { + do task::unkillable { + let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data); + let new_count = intrinsics::atomic_xsub(&mut data.count, 1) - 1; + assert!(new_count >= 0); + if new_count == 0 { + // drop glue takes over. + } else { + cast::forget(data); + } + } + } + } +} + + +/****************************************************************************/ + +#[allow(non_camel_case_types)] // runtime type +pub type rust_little_lock = *libc::c_void; + +struct LittleLock { + l: rust_little_lock, +} + +impl Drop for LittleLock { + fn finalize(&self) { + unsafe { + rust_destroy_little_lock(self.l); + } + } +} + +fn LittleLock() -> LittleLock { + unsafe { + LittleLock { + l: rust_create_little_lock() + } + } +} + +pub impl LittleLock { + #[inline(always)] + unsafe fn lock<T>(&self, f: &fn() -> T) -> T { + do atomically { + rust_lock_little_lock(self.l); + do (|| { + f() + }).finally { + rust_unlock_little_lock(self.l); + } + } + } +} + +struct ExData<T> { + lock: LittleLock, + failed: bool, + data: T, +} + +/** + * An arc over mutable data that is protected by a lock. For library use only. + */ +pub struct Exclusive<T> { + x: UnsafeAtomicRcBox<ExData<T>> +} + +pub fn exclusive<T:Owned>(user_data: T) -> Exclusive<T> { + let data = ExData { + lock: LittleLock(), + failed: false, + data: user_data + }; + Exclusive { + x: UnsafeAtomicRcBox::new(data) + } +} + +impl<T:Owned> Clone for Exclusive<T> { + // Duplicate an exclusive ARC, as std::arc::clone. + fn clone(&self) -> Exclusive<T> { + Exclusive { x: self.x.clone() } + } +} + +pub impl<T:Owned> Exclusive<T> { + // Exactly like std::arc::mutex_arc,access(), but with the little_lock + // instead of a proper mutex. Same reason for being unsafe. + // + // Currently, scheduling operations (i.e., yielding, receiving on a pipe, + // accessing the provided condition variable) are prohibited while inside + // the exclusive. Supporting that is a work in progress. + #[inline(always)] + unsafe fn with<U>(&self, f: &fn(x: &mut T) -> U) -> U { + let rec = self.x.get(); + do (*rec).lock.lock { + if (*rec).failed { + fail!("Poisoned exclusive - another task failed inside!"); + } + (*rec).failed = true; + let result = f(&mut (*rec).data); + (*rec).failed = false; + result + } + } + + #[inline(always)] + unsafe fn with_imm<U>(&self, f: &fn(x: &T) -> U) -> U { + do self.with |x| { + f(cast::transmute_immut(x)) + } + } +} + +fn compare_and_swap(address: &mut int, oldval: int, newval: int) -> bool { + unsafe { + let old = intrinsics::atomic_cxchg(address, oldval, newval); + old == oldval + } +} + +extern { + fn rust_create_little_lock() -> rust_little_lock; + fn rust_destroy_little_lock(lock: rust_little_lock); + fn rust_lock_little_lock(lock: rust_little_lock); + fn rust_unlock_little_lock(lock: rust_little_lock); +} + +#[cfg(test)] +mod tests { + use comm; + use super::exclusive; + use task; + use uint; + + #[test] + fn exclusive_arc() { + let mut futures = ~[]; + + let num_tasks = 10; + let count = 10; + + let total = exclusive(~0); + + for uint::range(0, num_tasks) |_i| { + let total = total.clone(); + let (port, chan) = comm::stream(); + futures.push(port); + + do task::spawn || { + for uint::range(0, count) |_i| { + do total.with |count| { + **count += 1; + } + } + chan.send(()); + } + }; + + for futures.each |f| { f.recv() } + + do total.with |total| { + assert!(**total == num_tasks * count) + }; + } + + #[test] #[should_fail] #[ignore(cfg(windows))] + fn exclusive_poison() { + // Tests that if one task fails inside of an exclusive, subsequent + // accesses will also fail. + let x = exclusive(1); + let x2 = x.clone(); + do task::try || { + do x2.with |one| { + assert_eq!(*one, 2); + } + }; + do x.with |one| { + assert_eq!(*one, 1); + } + } +} diff --git a/src/libstd/unstable/weak_task.rs b/src/libstd/unstable/weak_task.rs new file mode 100644 index 00000000000..d5c5230cef8 --- /dev/null +++ b/src/libstd/unstable/weak_task.rs @@ -0,0 +1,211 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! +Weak tasks + +Weak tasks are a runtime feature for building global services that +do not keep the runtime alive. Normally the runtime exits when all +tasks exits, but if a task is weak then the runtime may exit while +it is running, sending a notification to the task that the runtime +is trying to shut down. +*/ + +use cell::Cell; +use comm::{GenericSmartChan, stream}; +use comm::{Port, Chan, SharedChan, GenericChan, GenericPort}; +use hashmap::HashMap; +use option::{Some, None}; +use unstable::at_exit::at_exit; +use unstable::finally::Finally; +use unstable::global::global_data_clone_create; +use task::rt::{task_id, get_task_id}; +use task::task; + +#[cfg(test)] use task::spawn; + +type ShutdownMsg = (); + +// FIXME #4729: This could be a PortOne but I've experienced bugginess +// with oneshot pipes and try_send +pub unsafe fn weaken_task(f: &fn(Port<ShutdownMsg>)) { + let service = global_data_clone_create(global_data_key, + create_global_service); + let (shutdown_port, shutdown_chan) = stream::<ShutdownMsg>(); + let shutdown_port = Cell(shutdown_port); + let task = get_task_id(); + // Expect the weak task service to be alive + assert!(service.try_send(RegisterWeakTask(task, shutdown_chan))); + rust_dec_kernel_live_count(); + do (|| { + f(shutdown_port.take()) + }).finally || { + rust_inc_kernel_live_count(); + // Service my have already exited + service.send(UnregisterWeakTask(task)); + } +} + +type WeakTaskService = SharedChan<ServiceMsg>; +type TaskHandle = task_id; + +fn global_data_key(_v: WeakTaskService) { } + +enum ServiceMsg { + RegisterWeakTask(TaskHandle, Chan<ShutdownMsg>), + UnregisterWeakTask(TaskHandle), + Shutdown +} + +fn create_global_service() -> ~WeakTaskService { + + debug!("creating global weak task service"); + let (port, chan) = stream::<ServiceMsg>(); + let port = Cell(port); + let chan = SharedChan::new(chan); + let chan_clone = chan.clone(); + + let mut task = task(); + task.unlinked(); + do task.spawn { + debug!("running global weak task service"); + let port = Cell(port.take()); + do (|| { + let port = port.take(); + // The weak task service is itself a weak task + debug!("weakening the weak service task"); + unsafe { rust_dec_kernel_live_count(); } + run_weak_task_service(port); + }).finally { + debug!("unweakening the weak service task"); + unsafe { rust_inc_kernel_live_count(); } + } + } + + do at_exit { + debug!("shutting down weak task service"); + chan.send(Shutdown); + } + + return ~chan_clone; +} + +fn run_weak_task_service(port: Port<ServiceMsg>) { + + let mut shutdown_map = HashMap::new(); + + loop { + match port.recv() { + RegisterWeakTask(task, shutdown_chan) => { + let previously_unregistered = + shutdown_map.insert(task, shutdown_chan); + assert!(previously_unregistered); + } + UnregisterWeakTask(task) => { + match shutdown_map.pop(&task) { + Some(shutdown_chan) => { + // Oneshot pipes must send, even though + // nobody will receive this + shutdown_chan.send(()); + } + None => fail!() + } + } + Shutdown => break + } + } + + do shutdown_map.consume |_, shutdown_chan| { + // Weak task may have already exited + shutdown_chan.send(()); + } +} + +extern { + unsafe fn rust_inc_kernel_live_count(); + unsafe fn rust_dec_kernel_live_count(); +} + +#[test] +fn test_simple() { + let (port, chan) = stream(); + do spawn { + unsafe { + do weaken_task |_signal| { + } + } + chan.send(()); + } + port.recv(); +} + +#[test] +fn test_weak_weak() { + let (port, chan) = stream(); + do spawn { + unsafe { + do weaken_task |_signal| { + } + do weaken_task |_signal| { + } + } + chan.send(()); + } + port.recv(); +} + +#[test] +fn test_wait_for_signal() { + do spawn { + unsafe { + do weaken_task |signal| { + signal.recv(); + } + } + } +} + +#[test] +fn test_wait_for_signal_many() { + use uint; + for uint::range(0, 100) |_| { + do spawn { + unsafe { + do weaken_task |signal| { + signal.recv(); + } + } + } + } +} + +#[test] +fn test_select_stream_and_oneshot() { + use comm::select2i; + use either::{Left, Right}; + + let (port, chan) = stream(); + let port = Cell(port); + let (waitport, waitchan) = stream(); + do spawn { + unsafe { + do weaken_task |mut signal| { + let mut port = port.take(); + match select2i(&mut port, &mut signal) { + Left(*) => (), + Right(*) => fail!() + } + } + } + waitchan.send(()); + } + chan.send(()); + waitport.recv(); +} diff --git a/src/libstd/util.rs b/src/libstd/util.rs new file mode 100644 index 00000000000..e2b91594d12 --- /dev/null +++ b/src/libstd/util.rs @@ -0,0 +1,190 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +/*! + +Miscellaneous helpers for common patterns. + +*/ + +use prelude::*; +use unstable::intrinsics; + +/// The identity function. +#[inline(always)] +pub fn id<T>(x: T) -> T { x } + +/// Ignores a value. +#[inline(always)] +pub fn ignore<T>(_x: T) { } + +/// Sets `*ptr` to `new_value`, invokes `op()`, and then restores the +/// original value of `*ptr`. +/// +/// NB: This function accepts `@mut T` and not `&mut T` to avoid +/// an obvious borrowck hazard. Typically passing in `&mut T` will +/// cause borrow check errors because it freezes whatever location +/// that `&mut T` is stored in (either statically or dynamically). +#[inline(always)] +pub fn with<T,R>( + ptr: @mut T, + value: T, + op: &fn() -> R) -> R +{ + let prev = replace(ptr, value); + let result = op(); + *ptr = prev; + return result; +} + +/** + * Swap the values at two mutable locations of the same type, without + * deinitialising or copying either one. + */ +#[inline(always)] +pub fn swap<T>(x: &mut T, y: &mut T) { + unsafe { + swap_ptr(ptr::to_mut_unsafe_ptr(x), ptr::to_mut_unsafe_ptr(y)); + } +} + +/** + * Swap the values at two mutable locations of the same type, without + * deinitialising or copying either one. + */ +#[inline] +pub unsafe fn swap_ptr<T>(x: *mut T, y: *mut T) { + if x == y { return } + + // Give ourselves some scratch space to work with + let mut tmp: T = intrinsics::uninit(); + let t = ptr::to_mut_unsafe_ptr(&mut tmp); + + // Perform the swap + ptr::copy_memory(t, x, 1); + ptr::copy_memory(x, y, 1); + ptr::copy_memory(y, t, 1); + + // y and t now point to the same thing, but we need to completely forget t + // because it's no longer relevant. + cast::forget(tmp); +} + +/** + * Replace the value at a mutable location with a new one, returning the old + * value, without deinitialising or copying either one. + */ +#[inline(always)] +pub fn replace<T>(dest: &mut T, mut src: T) -> T { + swap(dest, &mut src); + src +} + +/** + * Replace the value at a mutable location with a new one, returning the old + * value, without deinitialising or copying either one. + */ +#[inline(always)] +pub unsafe fn replace_ptr<T>(dest: *mut T, mut src: T) -> T { + swap_ptr(dest, ptr::to_mut_unsafe_ptr(&mut src)); + src +} + +/// A non-copyable dummy type. +pub struct NonCopyable { + i: (), +} + +impl Drop for NonCopyable { + fn finalize(&self) { } +} + +pub fn NonCopyable() -> NonCopyable { NonCopyable { i: () } } + + +/// A type with no inhabitants +pub enum Void { } + +pub impl Void { + /// A utility function for ignoring this uninhabited type + fn uninhabited(self) -> ! { + match self { + // Nothing to match on + } + } +} + + +/** +A utility function for indicating unreachable code. It will fail if +executed. This is occasionally useful to put after loops that never +terminate normally, but instead directly return from a function. + +# Example + +~~~ +fn choose_weighted_item(v: &[Item]) -> Item { + assert!(!v.is_empty()); + let mut so_far = 0u; + for v.each |item| { + so_far += item.weight; + if so_far > 100 { + return item; + } + } + // The above loop always returns, so we must hint to the + // type checker that it isn't possible to get down here + util::unreachable(); +} +~~~ + +*/ +pub fn unreachable() -> ! { + fail!("internal error: entered unreachable code"); +} + +#[cfg(test)] +mod tests { + use option::{None, Some}; + use util::{Void, NonCopyable, id, replace, swap}; + use either::{Either, Left, Right}; + + #[test] + pub fn identity_crisis() { + // Writing a test for the identity function. How did it come to this? + let x = ~[(5, false)]; + //FIXME #3387 assert!(x.eq(id(copy x))); + let y = copy x; + assert!(x.eq(&id(y))); + } + #[test] + pub fn test_swap() { + let mut x = 31337; + let mut y = 42; + swap(&mut x, &mut y); + assert_eq!(x, 42); + assert_eq!(y, 31337); + } + #[test] + pub fn test_replace() { + let mut x = Some(NonCopyable()); + let y = replace(&mut x, None); + assert!(x.is_none()); + assert!(y.is_some()); + } + #[test] + pub fn test_uninhabited() { + let could_only_be_coin : Either <Void, ()> = Right (()); + match could_only_be_coin { + Right (coin) => coin, + Left (is_void) => is_void.uninhabited () + } + } +} diff --git a/src/libstd/uv.rs b/src/libstd/uv.rs deleted file mode 100644 index e055b407057..00000000000 --- a/src/libstd/uv.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * Rust bindings to libuv - * - * This is the base-module for various levels of bindings to - * the libuv library. - * - * These modules are seeing heavy work, currently, and the final - * API layout should not be inferred from its current form. - * - * This base module currently contains a historical, rust-based - * implementation of a few libuv operations that hews closely to - * the patterns of the libuv C-API. It was used, mostly, to explore - * some implementation details and will most likely be deprecated - * in the near future. - * - * The `ll` module contains low-level mappings for working directly - * with the libuv C-API. - * - * The `hl` module contains a set of tools library developers can - * use for interacting with an active libuv loop. This modules's - * API is meant to be used to write high-level, - * rust-idiomatic abstractions for utilizes libuv's asynchronous IO - * facilities. - */ - -pub use ll = super::uv_ll; -pub use iotask = uv_iotask; -pub use global_loop = uv_global_loop; diff --git a/src/libstd/uv_global_loop.rs b/src/libstd/uv_global_loop.rs deleted file mode 100644 index d1471ba1e77..00000000000 --- a/src/libstd/uv_global_loop.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! A process-wide libuv event loop for library use. - -use iotask = uv_iotask; -use uv_iotask::{IoTask, spawn_iotask}; - -use core::comm::Chan; -use core::option::{Some, None}; -use core::task::task; -use core::unstable::global::{global_data_clone_create, global_data_clone}; -use core::unstable::weak_task::weaken_task; - -/** - * Race-free helper to get access to a global task where a libuv - * loop is running. - * - * Use `uv::hl::interact` to do operations against the global - * loop that this function returns. - * - * # Return - * - * * A `hl::high_level_loop` that encapsulates communication with the global - * loop. - */ -pub fn get() -> IoTask { - return get_monitor_task_gl(); -} - -#[doc(hidden)] -fn get_monitor_task_gl() -> IoTask { - - type MonChan = Chan<IoTask>; - - struct GlobalIoTask(IoTask); - - impl Clone for GlobalIoTask { - fn clone(&self) -> GlobalIoTask { - GlobalIoTask((**self).clone()) - } - } - - fn key(_: GlobalIoTask) { } - - match unsafe { global_data_clone(key) } { - Some(GlobalIoTask(iotask)) => iotask, - None => { - let iotask: IoTask = spawn_loop(); - let mut installed = false; - let final_iotask = unsafe { - do global_data_clone_create(key) { - installed = true; - ~GlobalIoTask(iotask.clone()) - } - }; - if installed { - let mut task = task(); - task.unlinked(); - do task.spawn { - unsafe { - debug!("global monitor task starting"); - // As a weak task the runtime will notify us - // when to exit - do weaken_task |weak_exit_po| { - debug!("global monitor task is weak"); - weak_exit_po.recv(); - iotask::exit(&iotask); - debug!("global monitor task is unweak"); - }; - debug!("global monitor task exiting"); - } - } - } else { - iotask::exit(&iotask); - } - - match final_iotask { - GlobalIoTask(iotask) => iotask - } - } - } -} - -fn spawn_loop() -> IoTask { - let mut builder = task(); - - do builder.add_wrapper |task_body| { - let result: ~fn() = || { - // The I/O loop task also needs to be weak so it doesn't keep - // the runtime alive - unsafe { - do weaken_task |_| { - debug!("global libuv task is now weak"); - task_body(); - - // We don't wait for the exit message on weak_exit_po - // because the monitor task will tell the uv loop when to - // exit - - debug!("global libuv task is leaving weakened state"); - } - } - }; - result - }; - - builder.unlinked(); - spawn_iotask(builder) -} - -#[cfg(test)] -mod test { - - use get_gl = uv_global_loop::get; - use uv::iotask; - use uv::ll; - use uv_iotask::IoTask; - - use core::old_iter; - use core::libc; - use core::ptr; - use core::task; - use core::cast::transmute; - use core::libc::c_void; - use core::comm::{stream, SharedChan, Chan}; - - extern fn simple_timer_close_cb(timer_ptr: *ll::uv_timer_t) { - unsafe { - let exit_ch_ptr = ll::get_data_for_uv_handle( - timer_ptr as *libc::c_void); - let exit_ch = transmute::<*c_void, ~Chan<bool>>(exit_ch_ptr); - exit_ch.send(true); - debug!("EXIT_CH_PTR simple_timer_close_cb exit_ch_ptr: %?", - exit_ch_ptr); - } - } - extern fn simple_timer_cb(timer_ptr: *ll::uv_timer_t, - _status: libc::c_int) { - unsafe { - debug!(~"in simple timer cb"); - ll::timer_stop(timer_ptr); - let hl_loop = &get_gl(); - do iotask::interact(hl_loop) |_loop_ptr| { - debug!(~"closing timer"); - unsafe { - ll::close(timer_ptr, simple_timer_close_cb); - } - debug!(~"about to deref exit_ch_ptr"); - debug!(~"after msg sent on deref'd exit_ch"); - }; - debug!(~"exiting simple timer cb"); - } - } - - fn impl_uv_hl_simple_timer(iotask: &IoTask) { - unsafe { - let (exit_po, exit_ch) = stream::<bool>(); - let exit_ch_ptr: *libc::c_void = transmute(~exit_ch); - debug!("EXIT_CH_PTR newly created exit_ch_ptr: %?", - exit_ch_ptr); - let timer_handle = ll::timer_t(); - let timer_ptr: *ll::uv_timer_t = &timer_handle; - do iotask::interact(iotask) |loop_ptr| { - unsafe { - debug!(~"user code inside interact loop!!!"); - let init_status = ll::timer_init(loop_ptr, timer_ptr); - if(init_status == 0i32) { - ll::set_data_for_uv_handle( - timer_ptr as *libc::c_void, - exit_ch_ptr); - let start_status = ll::timer_start(timer_ptr, - simple_timer_cb, - 1u, 0u); - if(start_status != 0i32) { - fail!("failure on ll::timer_start()"); - } - } - else { - fail!("failure on ll::timer_init()"); - } - } - }; - exit_po.recv(); - debug!( - ~"global_loop timer test: msg recv on exit_po, done.."); - } - } - - #[test] - fn test_gl_uv_global_loop_high_level_global_timer() { - let hl_loop = &get_gl(); - let (exit_po, exit_ch) = stream::<()>(); - task::spawn_sched(task::ManualThreads(1u), || { - let hl_loop = &get_gl(); - impl_uv_hl_simple_timer(hl_loop); - exit_ch.send(()); - }); - impl_uv_hl_simple_timer(hl_loop); - exit_po.recv(); - } - - // keeping this test ignored until some kind of stress-test-harness - // is set up for the build bots - #[test] - #[ignore] - fn test_stress_gl_uv_global_loop_high_level_global_timer() { - let (exit_po, exit_ch) = stream::<()>(); - let exit_ch = SharedChan::new(exit_ch); - let cycles = 5000u; - for cycles.times { - let exit_ch_clone = exit_ch.clone(); - task::spawn_sched(task::ManualThreads(1u), || { - let hl_loop = &get_gl(); - impl_uv_hl_simple_timer(hl_loop); - exit_ch_clone.send(()); - }); - }; - for cycles.times { - exit_po.recv(); - }; - debug!(~"test_stress_gl_uv_global_loop_high_level_global_timer"+ - ~" exiting successfully!"); - } -} diff --git a/src/libstd/uv_iotask.rs b/src/libstd/uv_iotask.rs deleted file mode 100644 index 371eb338de3..00000000000 --- a/src/libstd/uv_iotask.rs +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * A task-based interface to the uv loop - * - * The I/O task runs in its own single-threaded scheduler. By using the - * `interact` function you can execute code in a uv callback. - */ - -use ll = uv_ll; - -use core::comm::{stream, Port, Chan, SharedChan}; -use core::libc::c_void; -use core::libc; - -/// Used to abstract-away direct interaction with a libuv loop. -pub struct IoTask { - async_handle: *ll::uv_async_t, - op_chan: SharedChan<IoTaskMsg> -} - -impl Clone for IoTask { - fn clone(&self) -> IoTask { - IoTask{ - async_handle: self.async_handle, - op_chan: self.op_chan.clone() - } - } -} - -pub fn spawn_iotask(mut task: task::TaskBuilder) -> IoTask { - let (iotask_port, iotask_chan) = stream(); - - task.sched_mode(task::SingleThreaded); - do task.spawn { - debug!("entering libuv task"); - run_loop(&iotask_chan); - debug!("libuv task exiting"); - }; - - iotask_port.recv() -} - - -/** - * Provide a callback to be processed by `iotask` - * - * The primary way to do operations again a running `iotask` that - * doesn't involve creating a uv handle via `safe_handle` - * - * # Warning - * - * This function is the only safe way to interact with _any_ `iotask`. - * Using functions in the `uv::ll` module outside of the `cb` passed into - * this function is _very dangerous_. - * - * # Arguments - * - * * iotask - a uv I/O task that you want to do operations against - * * cb - a function callback to be processed on the running loop's - * thread. The only parameter passed in is an opaque pointer representing the - * running `uv_loop_t*`. In the context of this callback, it is safe to use - * this pointer to do various uv_* API calls contained within the `uv::ll` - * module. It is not safe to send the `loop_ptr` param to this callback out - * via ports/chans. - */ -pub fn interact(iotask: &IoTask, cb: ~fn(*c_void)) { - send_msg(iotask, Interaction(cb)); -} - -/** - * Shut down the I/O task - * - * Is used to signal to the loop that it should close the internally-held - * async handle and do a sanity check to make sure that all other handles are - * closed, causing a failure otherwise. - */ -pub fn exit(iotask: &IoTask) { - send_msg(iotask, TeardownLoop); -} - - -// INTERNAL API - -enum IoTaskMsg { - Interaction(~fn(*libc::c_void)), - TeardownLoop -} - -/// Run the loop and begin handling messages -fn run_loop(iotask_ch: &Chan<IoTask>) { - - unsafe { - debug!("creating loop"); - let loop_ptr = ll::loop_new(); - - // set up the special async handle we'll use to allow multi-task - // communication with this loop - let async = ll::async_t(); - let async_handle: *ll::uv_async_t = &async; - - // associate the async handle with the loop - ll::async_init(loop_ptr, async_handle, wake_up_cb); - - let (msg_po, msg_ch) = stream::<IoTaskMsg>(); - - // initialize our loop data and store it in the loop - let data: IoTaskLoopData = IoTaskLoopData { - async_handle: async_handle, - msg_po: msg_po - }; - ll::set_data_for_uv_handle(async_handle, &data); - - // Send out a handle through which folks can talk to us - // while we dwell in the I/O loop - let iotask = IoTask { - async_handle: async_handle, - op_chan: SharedChan::new(msg_ch) - }; - iotask_ch.send(iotask); - - debug!("about to run uv loop"); - // enter the loop... this blocks until the loop is done.. - ll::run(loop_ptr); - debug!("uv loop ended"); - ll::loop_delete(loop_ptr); - } -} - -// data that lives for the lifetime of the high-evel oo -struct IoTaskLoopData { - async_handle: *ll::uv_async_t, - msg_po: Port<IoTaskMsg>, -} - -fn send_msg(iotask: &IoTask, - msg: IoTaskMsg) { - iotask.op_chan.send(msg); - unsafe { - ll::async_send(iotask.async_handle); - } -} - -/// Dispatch all pending messages -extern fn wake_up_cb(async_handle: *ll::uv_async_t, - status: int) { - - debug!("wake_up_cb extern.. handle: %? status: %?", - async_handle, status); - - unsafe { - let loop_ptr = ll::get_loop_for_uv_handle(async_handle); - let data = - ll::get_data_for_uv_handle(async_handle) as *IoTaskLoopData; - let msg_po = &(*data).msg_po; - - while msg_po.peek() { - match msg_po.recv() { - Interaction(ref cb) => (*cb)(loop_ptr), - TeardownLoop => begin_teardown(data) - } - } - } -} - -fn begin_teardown(data: *IoTaskLoopData) { - unsafe { - debug!("iotask begin_teardown() called, close async_handle"); - let async_handle = (*data).async_handle; - ll::close(async_handle as *c_void, tear_down_close_cb); - } -} -extern fn tear_down_walk_cb(handle: *libc::c_void, arg: *libc::c_void) { - debug!("IN TEARDOWN WALK CB"); - // pretty much, if we still have an active handle and it is *not* - // the async handle that facilities global loop communication, we - // want to barf out and fail - assert_eq!(handle, arg); -} - -extern fn tear_down_close_cb(handle: *ll::uv_async_t) { - unsafe { - let loop_ptr = ll::get_loop_for_uv_handle(handle); - debug!("in tear_down_close_cb"); - ll::walk(loop_ptr, tear_down_walk_cb, handle as *libc::c_void); - } -} - -#[cfg(test)] -extern fn async_close_cb(handle: *ll::uv_async_t) { - unsafe { - debug!("async_close_cb handle %?", handle); - let exit_ch = &(*(ll::get_data_for_uv_handle(handle) - as *AhData)).exit_ch; - let exit_ch = exit_ch.clone(); - exit_ch.send(()); - } -} - -#[cfg(test)] -extern fn async_handle_cb(handle: *ll::uv_async_t, status: libc::c_int) { - unsafe { - debug!("async_handle_cb handle %? status %?",handle,status); - ll::close(handle, async_close_cb); - } -} - -#[cfg(test)] -struct AhData { - iotask: IoTask, - exit_ch: SharedChan<()> -} - -#[cfg(test)] -fn impl_uv_iotask_async(iotask: &IoTask) { - let async_handle = ll::async_t(); - let ah_ptr: *ll::uv_async_t = &async_handle; - let (exit_po, exit_ch) = stream::<()>(); - let ah_data = AhData { - iotask: iotask.clone(), - exit_ch: SharedChan::new(exit_ch) - }; - let ah_data_ptr: *AhData = ptr::to_unsafe_ptr(&ah_data); - debug!("about to interact"); - do interact(iotask) |loop_ptr| { - unsafe { - debug!("interacting"); - ll::async_init(loop_ptr, ah_ptr, async_handle_cb); - ll::set_data_for_uv_handle( - ah_ptr, ah_data_ptr as *libc::c_void); - ll::async_send(ah_ptr); - } - }; - debug!("waiting for async close"); - exit_po.recv(); -} - -// this fn documents the bear minimum necessary to roll your own -// high_level_loop -#[cfg(test)] -fn spawn_test_loop(exit_ch: ~Chan<()>) -> IoTask { - let (iotask_port, iotask_ch) = stream::<IoTask>(); - do task::spawn_sched(task::ManualThreads(1u)) { - debug!("about to run a test loop"); - run_loop(&iotask_ch); - exit_ch.send(()); - }; - return iotask_port.recv(); -} - -#[cfg(test)] -extern fn lifetime_handle_close(handle: *libc::c_void) { - debug!("lifetime_handle_close ptr %?", handle); -} - -#[cfg(test)] -extern fn lifetime_async_callback(handle: *libc::c_void, - status: libc::c_int) { - debug!("lifetime_handle_close ptr %? status %?", - handle, status); -} - -#[test] -fn test_uv_iotask_async() { - let (exit_po, exit_ch) = stream::<()>(); - let iotask = &spawn_test_loop(~exit_ch); - - debug!("spawned iotask"); - - // using this handle to manage the lifetime of the - // high_level_loop, as it will exit the first time one of - // the impl_uv_hl_async() is cleaned up with no one ref'd - // handles on the loop (Which can happen under - // race-condition type situations.. this ensures that the - // loop lives until, at least, all of the - // impl_uv_hl_async() runs have been called, at least. - let (work_exit_po, work_exit_ch) = stream::<()>(); - let work_exit_ch = SharedChan::new(work_exit_ch); - for 7u.times { - let iotask_clone = iotask.clone(); - let work_exit_ch_clone = work_exit_ch.clone(); - do task::spawn_sched(task::ManualThreads(1u)) { - debug!("async"); - impl_uv_iotask_async(&iotask_clone); - debug!("done async"); - work_exit_ch_clone.send(()); - }; - }; - for 7u.times { - debug!("waiting"); - work_exit_po.recv(); - }; - debug!(~"sending teardown_loop msg.."); - exit(iotask); - exit_po.recv(); - debug!(~"after recv on exit_po.. exiting.."); -} diff --git a/src/libstd/uv_ll.rs b/src/libstd/uv_ll.rs deleted file mode 100644 index bc7703ec30a..00000000000 --- a/src/libstd/uv_ll.rs +++ /dev/null @@ -1,1910 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -/*! - * Low-level bindings to the libuv library. - * - * This module contains a set of direct, 'bare-metal' wrappers around - * the libuv C-API. - * - * Also contained herein are a set of rust records that map, in - * approximate memory-size, to the libuv data structures. The record - * implementations are adjusted, per-platform, to match their respective - * representations. - * - * There are also a collection of helper functions to ease interacting - * with the low-level API (such as a function to return the latest - * libuv error as a rust-formatted string). - * - * As new functionality, existant in uv.h, is added to the rust stdlib, - * the mappings should be added in this module. - * - * This module's implementation will hopefully be, eventually, replaced - * with per-platform, generated source files from rust-bindgen. - */ - -#[allow(non_camel_case_types)]; // C types - -use core::libc::size_t; -use core::libc::c_void; -use core::ptr::to_unsafe_ptr; - -pub type uv_handle_t = c_void; -pub type uv_loop_t = c_void; -pub type uv_idle_t = c_void; -pub type uv_idle_cb = *u8; - -// libuv struct mappings -pub struct uv_ip4_addr { - ip: ~[u8], - port: int, -} -pub type uv_ip6_addr = uv_ip4_addr; - -pub enum uv_handle_type { - UNKNOWN_HANDLE = 0, - UV_TCP, - UV_UDP, - UV_NAMED_PIPE, - UV_TTY, - UV_FILE, - UV_TIMER, - UV_PREPARE, - UV_CHECK, - UV_IDLE, - UV_ASYNC, - UV_ARES_TASK, - UV_ARES_EVENT, - UV_PROCESS, - UV_FS_EVENT -} - -pub type handle_type = libc::c_uint; - -pub struct uv_handle_fields { - loop_handle: *libc::c_void, - type_: handle_type, - close_cb: *u8, - data: *libc::c_void, -} - -// unix size: 8 -pub struct uv_err_t { - code: libc::c_int, - sys_errno_: libc::c_int -} - -// don't create one of these directly. instead, -// count on it appearing in libuv callbacks or embedded -// in other types as a pointer to be used in other -// operations (so mostly treat it as opaque, once you -// have it in this form..) -pub struct uv_stream_t { - fields: uv_handle_fields, -} - -// 64bit unix size: 216 -#[cfg(target_os="macos")] -pub struct uv_tcp_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, a13: *u8, a14: *u8, a15: *u8, - a16: *u8, a17: *u8, a18: *u8, a19: *u8, - a20: *u8, a21: *u8, a22: *u8, - a23: uv_tcp_t_osx_riders -} -#[cfg(target_arch="x86_64")] -pub struct uv_tcp_t_osx_riders { - a23: *u8, -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -pub struct uv_tcp_t_osx_riders { - a23: *u8, - a24: *u8, a25: *u8, -} -#[cfg(target_os="linux")] -#[cfg(target_os="freebsd")] -#[cfg(target_os="android")] -pub struct uv_tcp_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, a13: *u8, a14: *u8, a15: *u8, - a16: *u8, a17: *u8, a18: *u8, a19: *u8, - a20: *u8, a21: *u8, - a22: uv_tcp_t_32bit_unix_riders, -} -// 32bit unix size: 328 (164) -#[cfg(target_arch="x86_64")] -pub struct uv_tcp_t_32bit_unix_riders { - a29: *u8, -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -#[cfg(target_arch="mips")] -pub struct uv_tcp_t_32bit_unix_riders { - a29: *u8, a30: *u8, a31: *u8, -} - -// 32bit win32 size: 240 (120) -#[cfg(windows)] -pub struct uv_tcp_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, a13: *u8, a14: *u8, a15: *u8, - a16: *u8, a17: *u8, a18: *u8, a19: *u8, - a20: *u8, a21: *u8, a22: *u8, a23: *u8, - a24: *u8, a25: *u8, -} - -// unix size: 64 -#[cfg(unix)] -pub struct uv_connect_t { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8 -} -// win32 size: 88 (44) -#[cfg(windows)] -pub struct uv_connect_t { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, -} - -// unix size: 16 -pub struct uv_buf_t { - base: *u8, - len: libc::size_t, -} -// no gen stub method.. should create -// it via uv::direct::buf_init() - -// unix size: 160 -#[cfg(unix)] -pub struct uv_write_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, - a14: uv_write_t_32bit_unix_riders, -} -#[cfg(target_arch="x86_64")] -pub struct uv_write_t_32bit_unix_riders { - a13: *u8, a14: *u8, a15: *u8 -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -#[cfg(target_arch="mips")] -pub struct uv_write_t_32bit_unix_riders { - a13: *u8, a14: *u8, a15: *u8, - a16: *u8, -} -// win32 size: 136 (68) -#[cfg(windows)] -pub struct uv_write_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, -} -// 64bit unix size: 96 -// 32bit unix size: 152 (76) -#[cfg(unix)] -pub struct uv_async_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, - a07: uv_async_t_32bit_unix_riders, -} -#[cfg(target_arch="x86_64")] -pub struct uv_async_t_32bit_unix_riders { - a10: *u8, -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -#[cfg(target_arch="mips")] -pub struct uv_async_t_32bit_unix_riders { - a10: *u8, -} -// win32 size 132 (68) -#[cfg(windows)] -pub struct uv_async_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, - a12: *u8, -} - -// 64bit unix size: 120 -// 32bit unix size: 84 -#[cfg(unix)] -pub struct uv_timer_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, - a11: uv_timer_t_32bit_unix_riders, -} -#[cfg(target_arch="x86_64")] -pub struct uv_timer_t_32bit_unix_riders { - a10: *u8, -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -#[cfg(target_arch="mips")] -pub struct uv_timer_t_32bit_unix_riders { - a10: *u8, a11: *u8, a12: *u8 -} -// win32 size: 64 -#[cfg(windows)] -pub struct uv_timer_t { - fields: uv_handle_fields, - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - a08: *u8, a09: *u8, a10: *u8, a11: *u8, -} - -// unix size: 16 -pub struct sockaddr_in { - sin_family: u16, - sin_port: u16, - sin_addr: u32, // in_addr: this is an opaque, per-platform struct - sin_zero: (u8, u8, u8, u8, u8, u8, u8, u8), -} - -// unix size: 28 .. FIXME #1645 -// stuck with 32 because of rust padding structs? -#[cfg(target_arch="x86_64")] -pub struct sockaddr_in6 { - a0: *u8, a1: *u8, - a2: *u8, a3: *u8, -} -#[cfg(target_arch="x86")] -#[cfg(target_arch="arm")] -#[cfg(target_arch="mips")] -pub struct sockaddr_in6 { - a0: *u8, a1: *u8, - a2: *u8, a3: *u8, - a4: *u8, a5: *u8, - a6: *u8, a7: *u8, -} - -// unix size: 28 .. FIXME #1645 -// stuck with 32 because of rust padding structs? -pub type addr_in = addr_in_impl::addr_in; -#[cfg(unix)] -pub mod addr_in_impl { - #[cfg(target_arch="x86_64")] - pub struct addr_in { - a0: *u8, a1: *u8, - a2: *u8, a3: *u8, - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub struct addr_in { - a0: *u8, a1: *u8, - a2: *u8, a3: *u8, - a4: *u8, a5: *u8, - a6: *u8, a7: *u8, - } -} -#[cfg(windows)] -pub mod addr_in_impl { - pub struct addr_in { - a0: *u8, a1: *u8, - a2: *u8, a3: *u8, - } -} - -// unix size: 48, 32bit: 32 -pub type addrinfo = addrinfo_impl::addrinfo; -#[cfg(target_os="linux")] -#[cfg(target_os="android")] -pub mod addrinfo_impl { - #[cfg(target_arch="x86_64")] - pub struct addrinfo { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub struct addrinfo { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, a06: *u8, a07: *u8, - } -} -#[cfg(target_os="macos")] -#[cfg(target_os="freebsd")] -pub mod addrinfo_impl { - pub struct addrinfo { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, - } -} -#[cfg(windows)] -pub mod addrinfo_impl { - pub struct addrinfo { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, - a04: *u8, a05: *u8, - } -} - -// unix size: 72 -pub struct uv_getaddrinfo_t { - a00: *u8, a01: *u8, a02: *u8, a03: *u8, a04: *u8, a05: *u8, - a06: *u8, a07: *u8, a08: *u8, a09: *u8, - a10: *u8, a11: *u8, a12: *u8, a13: *u8, a14: *u8, a15: *u8 -} - -pub mod uv_ll_struct_stubgen { - - use core::ptr; - - use super::{ - uv_async_t, - uv_connect_t, - uv_getaddrinfo_t, - uv_handle_fields, - uv_tcp_t, - uv_timer_t, - uv_write_t, - }; - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - use super::{ - uv_async_t_32bit_unix_riders, - uv_timer_t_32bit_unix_riders, - uv_write_t_32bit_unix_riders, - }; - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - #[cfg(target_os = "freebsd")] - use super::uv_tcp_t_32bit_unix_riders; - - pub fn gen_stub_uv_tcp_t() -> uv_tcp_t { - return gen_stub_os(); - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - #[cfg(target_os = "freebsd")] - pub fn gen_stub_os() -> uv_tcp_t { - return gen_stub_arch(); - #[cfg(target_arch="x86_64")] - pub fn gen_stub_arch() -> uv_tcp_t { - uv_tcp_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8, - a19: 0 as *u8, - a20: 0 as *u8, a21: 0 as *u8, - a22: uv_tcp_t_32bit_unix_riders { a29: 0 as *u8 }, - } - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub fn gen_stub_arch() -> uv_tcp_t { - uv_tcp_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8, - a19: 0 as *u8, - a20: 0 as *u8, a21: 0 as *u8, - a22: uv_tcp_t_32bit_unix_riders { - a29: 0 as *u8, a30: 0 as *u8, a31: 0 as *u8, - }, - } - } - } - #[cfg(windows)] - pub fn gen_stub_os() -> uv_tcp_t { - uv_tcp_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8, - a19: 0 as *u8, - a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8, - a23: 0 as *u8, - a24: 0 as *u8, a25: 0 as *u8, - } - } - #[cfg(target_os = "macos")] - pub fn gen_stub_os() -> uv_tcp_t { - use super::uv_tcp_t_osx_riders; - - return gen_stub_arch(); - - #[cfg(target_arch = "x86_64")] - fn gen_stub_arch() -> uv_tcp_t { - uv_tcp_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8, - a19: 0 as *u8, - a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8, - a23: uv_tcp_t_osx_riders { - a23: 0 as *u8, - } - } - } - - #[cfg(target_arch = "x86")] - #[cfg(target_arch = "arm")] - fn gen_stub_arch() -> uv_tcp_t { - uv_tcp_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8, - a19: 0 as *u8, - a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8, - a23: uv_tcp_t_osx_riders { - a23: 0 as *u8, - a24: 0 as *u8, a25: 0 as *u8, - } - } - } - } - } - #[cfg(unix)] - pub fn gen_stub_uv_connect_t() -> uv_connect_t { - uv_connect_t { - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8 - } - } - #[cfg(windows)] - pub fn gen_stub_uv_connect_t() -> uv_connect_t { - uv_connect_t { - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - } - } - #[cfg(unix)] - pub fn gen_stub_uv_async_t() -> uv_async_t { - return gen_stub_arch(); - #[cfg(target_arch = "x86_64")] - pub fn gen_stub_arch() -> uv_async_t { - uv_async_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: uv_async_t_32bit_unix_riders { a10: 0 as *u8 }, - } - } - #[cfg(target_arch = "x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub fn gen_stub_arch() -> uv_async_t { - uv_async_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: uv_async_t_32bit_unix_riders { - a10: 0 as *u8, - } - } - } - } - #[cfg(windows)] - pub fn gen_stub_uv_async_t() -> uv_async_t { - uv_async_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, - } - } - #[cfg(unix)] - pub fn gen_stub_uv_timer_t() -> uv_timer_t { - return gen_stub_arch(); - #[cfg(target_arch = "x86_64")] - pub fn gen_stub_arch() -> uv_timer_t { - uv_timer_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, - a11: uv_timer_t_32bit_unix_riders { - a10: 0 as *u8 - }, - } - } - #[cfg(target_arch = "x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub fn gen_stub_arch() -> uv_timer_t { - uv_timer_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, - a11: uv_timer_t_32bit_unix_riders { - a10: 0 as *u8, a11: 0 as *u8, - a12: 0 as *u8, - }, - } - } - } - #[cfg(windows)] - pub fn gen_stub_uv_timer_t() -> uv_timer_t { - uv_timer_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - } - } - #[cfg(unix)] - pub fn gen_stub_uv_write_t() -> uv_write_t { - return gen_stub_arch(); - #[cfg(target_arch="x86_64")] - pub fn gen_stub_arch() -> uv_write_t { - uv_write_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, - a14: uv_write_t_32bit_unix_riders { a13: 0 as *u8, - a14: 0 as *u8, - a15: 0 as *u8}, - } - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - pub fn gen_stub_arch() -> uv_write_t { - uv_write_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8, - a14: uv_write_t_32bit_unix_riders { - a13: 0 as *u8, - a14: 0 as *u8, - a15: 0 as *u8, - a16: 0 as *u8, - } - } - } - } - #[cfg(windows)] - pub fn gen_stub_uv_write_t() -> uv_write_t { - uv_write_t { - fields: uv_handle_fields { - loop_handle: ptr::null(), type_: 0u32, - close_cb: ptr::null(), - data: ptr::null(), - }, - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, - a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, - a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8, - a11: 0 as *u8, - a12: 0 as *u8 - } - } - pub fn gen_stub_uv_getaddrinfo_t() -> uv_getaddrinfo_t { - uv_getaddrinfo_t { - a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, a03: 0 as *u8, - a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, a07: 0 as *u8, - a08: 0 as *u8, a09: 0 as *u8, - a10: 1 as *u8, a11: 1 as *u8, a12: 1 as *u8, a13: 1 as *u8, - a14: 1 as *u8, a15: 1 as *u8 - } - } -} - -#[nolink] -extern { - // libuv public API - unsafe fn rust_uv_loop_new() -> *libc::c_void; - unsafe fn rust_uv_loop_delete(lp: *libc::c_void); - unsafe fn rust_uv_run(loop_handle: *libc::c_void); - unsafe fn rust_uv_close(handle: *libc::c_void, cb: *u8); - unsafe fn rust_uv_walk(loop_handle: *libc::c_void, cb: *u8, - arg: *libc::c_void); - - unsafe fn rust_uv_idle_new() -> *uv_idle_t; - unsafe fn rust_uv_idle_delete(handle: *uv_idle_t); - unsafe fn rust_uv_idle_init(loop_handle: *uv_loop_t, - handle: *uv_idle_t) -> libc::c_int; - unsafe fn rust_uv_idle_start(handle: *uv_idle_t, - cb: uv_idle_cb) -> libc::c_int; - unsafe fn rust_uv_idle_stop(handle: *uv_idle_t) -> libc::c_int; - - unsafe fn rust_uv_async_send(handle: *uv_async_t); - unsafe fn rust_uv_async_init(loop_handle: *libc::c_void, - async_handle: *uv_async_t, - cb: *u8) -> libc::c_int; - unsafe fn rust_uv_tcp_init( - loop_handle: *libc::c_void, - handle_ptr: *uv_tcp_t) -> libc::c_int; - // FIXME ref #2604 .. ? - unsafe fn rust_uv_buf_init(out_buf: *uv_buf_t, base: *u8, - len: libc::size_t); - unsafe fn rust_uv_last_error(loop_handle: *libc::c_void) -> uv_err_t; - // FIXME ref #2064 - unsafe fn rust_uv_strerror(err: *uv_err_t) -> *libc::c_char; - // FIXME ref #2064 - unsafe fn rust_uv_err_name(err: *uv_err_t) -> *libc::c_char; - unsafe fn rust_uv_ip4_addr(ip: *u8, port: libc::c_int) - -> sockaddr_in; - unsafe fn rust_uv_ip6_addr(ip: *u8, port: libc::c_int) - -> sockaddr_in6; - unsafe fn rust_uv_ip4_name(src: *sockaddr_in, - dst: *u8, - size: libc::size_t) - -> libc::c_int; - unsafe fn rust_uv_ip6_name(src: *sockaddr_in6, - dst: *u8, - size: libc::size_t) - -> libc::c_int; - unsafe fn rust_uv_ip4_port(src: *sockaddr_in) -> libc::c_uint; - unsafe fn rust_uv_ip6_port(src: *sockaddr_in6) -> libc::c_uint; - // FIXME ref #2064 - unsafe fn rust_uv_tcp_connect(connect_ptr: *uv_connect_t, - tcp_handle_ptr: *uv_tcp_t, - after_cb: *u8, - addr: *sockaddr_in) - -> libc::c_int; - // FIXME ref #2064 - unsafe fn rust_uv_tcp_bind(tcp_server: *uv_tcp_t, - addr: *sockaddr_in) -> libc::c_int; - // FIXME ref #2064 - unsafe fn rust_uv_tcp_connect6(connect_ptr: *uv_connect_t, - tcp_handle_ptr: *uv_tcp_t, - after_cb: *u8, - addr: *sockaddr_in6) -> libc::c_int; - // FIXME ref #2064 - unsafe fn rust_uv_tcp_bind6(tcp_server: *uv_tcp_t, - addr: *sockaddr_in6) -> libc::c_int; - unsafe fn rust_uv_tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, - name: *sockaddr_in) -> libc::c_int; - unsafe fn rust_uv_tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, - name: *sockaddr_in6) ->libc::c_int; - unsafe fn rust_uv_listen(stream: *libc::c_void, - backlog: libc::c_int, - cb: *u8) -> libc::c_int; - unsafe fn rust_uv_accept(server: *libc::c_void, client: *libc::c_void) - -> libc::c_int; - unsafe fn rust_uv_write(req: *libc::c_void, - stream: *libc::c_void, - buf_in: *uv_buf_t, - buf_cnt: libc::c_int, - cb: *u8) - -> libc::c_int; - unsafe fn rust_uv_read_start(stream: *libc::c_void, - on_alloc: *u8, - on_read: *u8) - -> libc::c_int; - unsafe fn rust_uv_read_stop(stream: *libc::c_void) -> libc::c_int; - unsafe fn rust_uv_timer_init(loop_handle: *libc::c_void, - timer_handle: *uv_timer_t) - -> libc::c_int; - unsafe fn rust_uv_timer_start( - timer_handle: *uv_timer_t, - cb: *u8, - timeout: libc::uint64_t, - repeat: libc::uint64_t) -> libc::c_int; - unsafe fn rust_uv_timer_stop(handle: *uv_timer_t) -> libc::c_int; - - unsafe fn rust_uv_getaddrinfo(loop_ptr: *libc::c_void, - handle: *uv_getaddrinfo_t, - cb: *u8, - node_name_ptr: *u8, - service_name_ptr: *u8, - // should probably only pass ptr::null() - hints: *addrinfo) - -> libc::c_int; - unsafe fn rust_uv_freeaddrinfo(res: *addrinfo); - - // data accessors/helpers for rust-mapped uv structs - unsafe fn rust_uv_helper_get_INADDR_NONE() -> u32; - unsafe fn rust_uv_is_ipv4_addrinfo(input: *addrinfo) -> bool; - unsafe fn rust_uv_is_ipv6_addrinfo(input: *addrinfo) -> bool; - unsafe fn rust_uv_get_next_addrinfo(input: *addrinfo) -> *addrinfo; - unsafe fn rust_uv_addrinfo_as_sockaddr_in(input: *addrinfo) - -> *sockaddr_in; - unsafe fn rust_uv_addrinfo_as_sockaddr_in6(input: *addrinfo) - -> *sockaddr_in6; - unsafe fn rust_uv_malloc_buf_base_of(sug_size: libc::size_t) -> *u8; - unsafe fn rust_uv_free_base_of_buf(buf: uv_buf_t); - unsafe fn rust_uv_get_stream_handle_from_connect_req( - connect_req: *uv_connect_t) - -> *uv_stream_t; - unsafe fn rust_uv_get_stream_handle_from_write_req( - write_req: *uv_write_t) - -> *uv_stream_t; - unsafe fn rust_uv_get_loop_for_uv_handle(handle: *libc::c_void) - -> *libc::c_void; - unsafe fn rust_uv_get_data_for_uv_loop(loop_ptr: *libc::c_void) - -> *libc::c_void; - unsafe fn rust_uv_set_data_for_uv_loop(loop_ptr: *libc::c_void, - data: *libc::c_void); - unsafe fn rust_uv_get_data_for_uv_handle(handle: *libc::c_void) - -> *libc::c_void; - unsafe fn rust_uv_set_data_for_uv_handle(handle: *libc::c_void, - data: *libc::c_void); - unsafe fn rust_uv_get_data_for_req(req: *libc::c_void) - -> *libc::c_void; - unsafe fn rust_uv_set_data_for_req(req: *libc::c_void, - data: *libc::c_void); - unsafe fn rust_uv_get_base_from_buf(buf: uv_buf_t) -> *u8; - unsafe fn rust_uv_get_len_from_buf(buf: uv_buf_t) -> libc::size_t; - - // sizeof testing helpers - unsafe fn rust_uv_helper_uv_tcp_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_connect_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_buf_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_write_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_err_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_sockaddr_in_size() -> libc::c_uint; - unsafe fn rust_uv_helper_sockaddr_in6_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_async_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_timer_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_uv_getaddrinfo_t_size() -> libc::c_uint; - unsafe fn rust_uv_helper_addrinfo_size() -> libc::c_uint; - unsafe fn rust_uv_helper_addr_in_size() -> libc::c_uint; -} - -pub unsafe fn loop_new() -> *libc::c_void { - return rust_uv_loop_new(); -} - -pub unsafe fn loop_delete(loop_handle: *libc::c_void) { - rust_uv_loop_delete(loop_handle); -} - -pub unsafe fn run(loop_handle: *libc::c_void) { - rust_uv_run(loop_handle); -} - -pub unsafe fn close<T>(handle: *T, cb: *u8) { - rust_uv_close(handle as *libc::c_void, cb); -} - -pub unsafe fn walk(loop_handle: *libc::c_void, cb: *u8, arg: *libc::c_void) { - rust_uv_walk(loop_handle, cb, arg); -} - -pub unsafe fn idle_new() -> *uv_idle_t { - rust_uv_idle_new() -} - -pub unsafe fn idle_delete(handle: *uv_idle_t) { - rust_uv_idle_delete(handle) -} - -pub unsafe fn idle_init(loop_handle: *uv_loop_t, - handle: *uv_idle_t) -> libc::c_int { - rust_uv_idle_init(loop_handle, handle) -} - -pub unsafe fn idle_start(handle: *uv_idle_t, cb: uv_idle_cb) -> libc::c_int { - rust_uv_idle_start(handle, cb) -} - -pub unsafe fn idle_stop(handle: *uv_idle_t) -> libc::c_int { - rust_uv_idle_stop(handle) -} - -pub unsafe fn tcp_init(loop_handle: *libc::c_void, handle: *uv_tcp_t) - -> libc::c_int { - return rust_uv_tcp_init(loop_handle, handle); -} -// FIXME ref #2064 -pub unsafe fn tcp_connect(connect_ptr: *uv_connect_t, - tcp_handle_ptr: *uv_tcp_t, - addr_ptr: *sockaddr_in, - after_connect_cb: *u8) --> libc::c_int { - return rust_uv_tcp_connect(connect_ptr, tcp_handle_ptr, - after_connect_cb, addr_ptr); -} -// FIXME ref #2064 -pub unsafe fn tcp_connect6(connect_ptr: *uv_connect_t, - tcp_handle_ptr: *uv_tcp_t, - addr_ptr: *sockaddr_in6, - after_connect_cb: *u8) --> libc::c_int { - return rust_uv_tcp_connect6(connect_ptr, tcp_handle_ptr, - after_connect_cb, addr_ptr); -} -// FIXME ref #2064 -pub unsafe fn tcp_bind(tcp_server_ptr: *uv_tcp_t, - addr_ptr: *sockaddr_in) -> libc::c_int { - return rust_uv_tcp_bind(tcp_server_ptr, - addr_ptr); -} -// FIXME ref #2064 -pub unsafe fn tcp_bind6(tcp_server_ptr: *uv_tcp_t, - addr_ptr: *sockaddr_in6) -> libc::c_int { - return rust_uv_tcp_bind6(tcp_server_ptr, - addr_ptr); -} - -pub unsafe fn tcp_getpeername(tcp_handle_ptr: *uv_tcp_t, - name: *sockaddr_in) -> libc::c_int { - return rust_uv_tcp_getpeername(tcp_handle_ptr, name); -} - -pub unsafe fn tcp_getpeername6(tcp_handle_ptr: *uv_tcp_t, - name: *sockaddr_in6) ->libc::c_int { - return rust_uv_tcp_getpeername6(tcp_handle_ptr, name); -} - -pub unsafe fn listen<T>(stream: *T, backlog: libc::c_int, - cb: *u8) -> libc::c_int { - return rust_uv_listen(stream as *libc::c_void, backlog, cb); -} - -pub unsafe fn accept(server: *libc::c_void, client: *libc::c_void) - -> libc::c_int { - return rust_uv_accept(server as *libc::c_void, - client as *libc::c_void); -} - -pub unsafe fn write<T>(req: *uv_write_t, stream: *T, - buf_in: *~[uv_buf_t], cb: *u8) -> libc::c_int { - let buf_ptr = vec::raw::to_ptr(*buf_in); - let buf_cnt = vec::len(*buf_in) as i32; - return rust_uv_write(req as *libc::c_void, - stream as *libc::c_void, - buf_ptr, buf_cnt, cb); -} -pub unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8, - on_read: *u8) -> libc::c_int { - return rust_uv_read_start(stream as *libc::c_void, - on_alloc, on_read); -} - -pub unsafe fn read_stop(stream: *uv_stream_t) -> libc::c_int { - return rust_uv_read_stop(stream as *libc::c_void); -} - -pub unsafe fn last_error(loop_handle: *libc::c_void) -> uv_err_t { - return rust_uv_last_error(loop_handle); -} - -pub unsafe fn strerror(err: *uv_err_t) -> *libc::c_char { - return rust_uv_strerror(err); -} -pub unsafe fn err_name(err: *uv_err_t) -> *libc::c_char { - return rust_uv_err_name(err); -} - -pub unsafe fn async_init(loop_handle: *libc::c_void, - async_handle: *uv_async_t, - cb: *u8) -> libc::c_int { - return rust_uv_async_init(loop_handle, - async_handle, - cb); -} - -pub unsafe fn async_send(async_handle: *uv_async_t) { - return rust_uv_async_send(async_handle); -} -pub unsafe fn buf_init(input: *u8, len: uint) -> uv_buf_t { - let out_buf = uv_buf_t { base: ptr::null(), len: 0 as libc::size_t }; - let out_buf_ptr: *uv_buf_t = &out_buf; - rust_uv_buf_init(out_buf_ptr, input, len as size_t); - return out_buf; -} -pub unsafe fn ip4_addr(ip: &str, port: int) -> sockaddr_in { - do str::as_c_str(ip) |ip_buf| { - rust_uv_ip4_addr(ip_buf as *u8, - port as libc::c_int) - } -} -pub unsafe fn ip6_addr(ip: &str, port: int) -> sockaddr_in6 { - do str::as_c_str(ip) |ip_buf| { - rust_uv_ip6_addr(ip_buf as *u8, - port as libc::c_int) - } -} -pub unsafe fn ip4_name(src: &sockaddr_in) -> ~str { - // ipv4 addr max size: 15 + 1 trailing null byte - let dst: ~[u8] = ~[0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8]; - do vec::as_imm_buf(dst) |dst_buf, size| { - rust_uv_ip4_name(to_unsafe_ptr(src), - dst_buf, size as libc::size_t); - // seems that checking the result of uv_ip4_name - // doesn't work too well.. - // you're stuck looking at the value of dst_buf - // to see if it is the string representation of - // INADDR_NONE (0xffffffff or 255.255.255.255 on - // many platforms) - str::raw::from_buf(dst_buf) - } -} -pub unsafe fn ip6_name(src: &sockaddr_in6) -> ~str { - // ipv6 addr max size: 45 + 1 trailing null byte - let dst: ~[u8] = ~[0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8]; - do vec::as_imm_buf(dst) |dst_buf, size| { - let src_unsafe_ptr = to_unsafe_ptr(src); - let result = rust_uv_ip6_name(src_unsafe_ptr, - dst_buf, size as libc::size_t); - match result { - 0i32 => str::raw::from_buf(dst_buf), - _ => ~"" - } - } -} -pub unsafe fn ip4_port(src: &sockaddr_in) -> uint { - rust_uv_ip4_port(to_unsafe_ptr(src)) as uint -} -pub unsafe fn ip6_port(src: &sockaddr_in6) -> uint { - rust_uv_ip6_port(to_unsafe_ptr(src)) as uint -} - -pub unsafe fn timer_init(loop_ptr: *libc::c_void, - timer_ptr: *uv_timer_t) -> libc::c_int { - return rust_uv_timer_init(loop_ptr, timer_ptr); -} -pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint, - repeat: uint) -> libc::c_int { - return rust_uv_timer_start(timer_ptr, cb, timeout as libc::uint64_t, - repeat as libc::uint64_t); -} -pub unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> libc::c_int { - return rust_uv_timer_stop(timer_ptr); -} -pub unsafe fn getaddrinfo(loop_ptr: *libc::c_void, - handle: *uv_getaddrinfo_t, - cb: *u8, - node_name_ptr: *u8, - service_name_ptr: *u8, - hints: *addrinfo) -> libc::c_int { - rust_uv_getaddrinfo(loop_ptr, - handle, - cb, - node_name_ptr, - service_name_ptr, - hints) -} -pub unsafe fn freeaddrinfo(res: *addrinfo) { - rust_uv_freeaddrinfo(res); -} - -// libuv struct initializers -pub fn tcp_t() -> uv_tcp_t { - return uv_ll_struct_stubgen::gen_stub_uv_tcp_t(); -} -pub fn connect_t() -> uv_connect_t { - return uv_ll_struct_stubgen::gen_stub_uv_connect_t(); -} -pub fn write_t() -> uv_write_t { - return uv_ll_struct_stubgen::gen_stub_uv_write_t(); -} -pub fn async_t() -> uv_async_t { - return uv_ll_struct_stubgen::gen_stub_uv_async_t(); -} -pub fn timer_t() -> uv_timer_t { - return uv_ll_struct_stubgen::gen_stub_uv_timer_t(); -} -pub fn getaddrinfo_t() -> uv_getaddrinfo_t { - return uv_ll_struct_stubgen::gen_stub_uv_getaddrinfo_t(); -} - -// data access helpers -pub unsafe fn get_loop_for_uv_handle<T>(handle: *T) - -> *libc::c_void { - return rust_uv_get_loop_for_uv_handle(handle as *libc::c_void); -} -pub unsafe fn get_stream_handle_from_connect_req(connect: *uv_connect_t) - -> *uv_stream_t { - return rust_uv_get_stream_handle_from_connect_req( - connect); -} -pub unsafe fn get_stream_handle_from_write_req( - write_req: *uv_write_t) - -> *uv_stream_t { - return rust_uv_get_stream_handle_from_write_req( - write_req); -} -pub unsafe fn get_data_for_uv_loop(loop_ptr: *libc::c_void) -> *libc::c_void { - rust_uv_get_data_for_uv_loop(loop_ptr) -} -pub unsafe fn set_data_for_uv_loop(loop_ptr: *libc::c_void, - data: *libc::c_void) { - rust_uv_set_data_for_uv_loop(loop_ptr, data); -} -pub unsafe fn get_data_for_uv_handle<T>(handle: *T) -> *libc::c_void { - return rust_uv_get_data_for_uv_handle(handle as *libc::c_void); -} -pub unsafe fn set_data_for_uv_handle<T, U>(handle: *T, data: *U) { - rust_uv_set_data_for_uv_handle(handle as *libc::c_void, - data as *libc::c_void); -} -pub unsafe fn get_data_for_req<T>(req: *T) -> *libc::c_void { - return rust_uv_get_data_for_req(req as *libc::c_void); -} -pub unsafe fn set_data_for_req<T, U>(req: *T, - data: *U) { - rust_uv_set_data_for_req(req as *libc::c_void, - data as *libc::c_void); -} -pub unsafe fn get_base_from_buf(buf: uv_buf_t) -> *u8 { - return rust_uv_get_base_from_buf(buf); -} -pub unsafe fn get_len_from_buf(buf: uv_buf_t) -> libc::size_t { - return rust_uv_get_len_from_buf(buf); -} -pub unsafe fn malloc_buf_base_of(suggested_size: libc::size_t) - -> *u8 { - return rust_uv_malloc_buf_base_of(suggested_size); -} -pub unsafe fn free_base_of_buf(buf: uv_buf_t) { - rust_uv_free_base_of_buf(buf); -} - -pub unsafe fn get_last_err_info(uv_loop: *libc::c_void) -> ~str { - let err = last_error(uv_loop); - let err_ptr: *uv_err_t = &err; - let err_name = str::raw::from_c_str(err_name(err_ptr)); - let err_msg = str::raw::from_c_str(strerror(err_ptr)); - return fmt!("LIBUV ERROR: name: %s msg: %s", - err_name, err_msg); -} - -pub unsafe fn get_last_err_data(uv_loop: *libc::c_void) -> uv_err_data { - let err = last_error(uv_loop); - let err_ptr: *uv_err_t = &err; - let err_name = str::raw::from_c_str(err_name(err_ptr)); - let err_msg = str::raw::from_c_str(strerror(err_ptr)); - uv_err_data { err_name: err_name, err_msg: err_msg } -} - -pub struct uv_err_data { - err_name: ~str, - err_msg: ~str, -} - -pub unsafe fn is_ipv4_addrinfo(input: *addrinfo) -> bool { - rust_uv_is_ipv4_addrinfo(input) -} -pub unsafe fn is_ipv6_addrinfo(input: *addrinfo) -> bool { - rust_uv_is_ipv6_addrinfo(input) -} -pub unsafe fn get_INADDR_NONE() -> u32 { - rust_uv_helper_get_INADDR_NONE() -} -pub unsafe fn get_next_addrinfo(input: *addrinfo) -> *addrinfo { - rust_uv_get_next_addrinfo(input) -} -pub unsafe fn addrinfo_as_sockaddr_in(input: *addrinfo) -> *sockaddr_in { - rust_uv_addrinfo_as_sockaddr_in(input) -} -pub unsafe fn addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6 { - rust_uv_addrinfo_as_sockaddr_in6(input) -} - -#[cfg(test)] -mod test { - use core::comm::{SharedChan, stream, GenericChan, GenericPort}; - use super::*; - - enum tcp_read_data { - tcp_read_eof, - tcp_read_more(~[u8]), - tcp_read_error - } - - struct request_wrapper { - write_req: *uv_write_t, - req_buf: *~[uv_buf_t], - read_chan: SharedChan<~str>, - } - - extern fn after_close_cb(handle: *libc::c_void) { - debug!("after uv_close! handle ptr: %?", - handle); - } - - extern fn on_alloc_cb(handle: *libc::c_void, - suggested_size: libc::size_t) - -> uv_buf_t { - unsafe { - debug!(~"on_alloc_cb!"); - let char_ptr = malloc_buf_base_of(suggested_size); - debug!("on_alloc_cb h: %? char_ptr: %u sugsize: %u", - handle, - char_ptr as uint, - suggested_size as uint); - return buf_init(char_ptr, suggested_size as uint); - } - } - - extern fn on_read_cb(stream: *uv_stream_t, - nread: libc::ssize_t, - buf: uv_buf_t) { - unsafe { - let nread = nread as int; - debug!("CLIENT entering on_read_cb nred: %d", - nread); - if (nread > 0) { - // we have data - debug!("CLIENT read: data! nread: %d", nread); - read_stop(stream); - let client_data = - get_data_for_uv_handle(stream as *libc::c_void) - as *request_wrapper; - let buf_base = get_base_from_buf(buf); - let bytes = vec::from_buf(buf_base, nread as uint); - let read_chan = (*client_data).read_chan.clone(); - let msg_from_server = str::from_bytes(bytes); - read_chan.send(msg_from_server); - close(stream as *libc::c_void, after_close_cb) - } - else if (nread == -1) { - // err .. possibly EOF - debug!(~"read: eof!"); - } - else { - // nread == 0 .. do nothing, just free buf as below - debug!(~"read: do nothing!"); - } - // when we're done - free_base_of_buf(buf); - debug!(~"CLIENT exiting on_read_cb"); - } - } - - extern fn on_write_complete_cb(write_req: *uv_write_t, - status: libc::c_int) { - unsafe { - debug!( - "CLIENT beginning on_write_complete_cb status: %d", - status as int); - let stream = get_stream_handle_from_write_req(write_req); - debug!( - "CLIENT on_write_complete_cb: tcp:%d write_handle:%d", - stream as int, write_req as int); - let result = read_start(stream, on_alloc_cb, on_read_cb); - debug!("CLIENT ending on_write_complete_cb .. status: %d", - result as int); - } - } - - extern fn on_connect_cb(connect_req_ptr: *uv_connect_t, - status: libc::c_int) { - unsafe { - debug!("beginning on_connect_cb .. status: %d", - status as int); - let stream = - get_stream_handle_from_connect_req(connect_req_ptr); - if (status == 0i32) { - debug!(~"on_connect_cb: in status=0 if.."); - let client_data = get_data_for_req( - connect_req_ptr as *libc::c_void) - as *request_wrapper; - let write_handle = (*client_data).write_req; - debug!("on_connect_cb: tcp: %d write_hdl: %d", - stream as int, write_handle as int); - let write_result = write(write_handle, - stream as *libc::c_void, - (*client_data).req_buf, - on_write_complete_cb); - debug!("on_connect_cb: write() status: %d", - write_result as int); - } - else { - let test_loop = get_loop_for_uv_handle( - stream as *libc::c_void); - let err_msg = get_last_err_info(test_loop); - debug!(err_msg); - assert!(false); - } - debug!(~"finishing on_connect_cb"); - } - } - - fn impl_uv_tcp_request(ip: &str, port: int, req_str: &str, - client_chan: SharedChan<~str>) { - unsafe { - let test_loop = loop_new(); - let tcp_handle = tcp_t(); - let tcp_handle_ptr: *uv_tcp_t = &tcp_handle; - let connect_handle = connect_t(); - let connect_req_ptr: *uv_connect_t = &connect_handle; - - // this is the persistent payload of data that we - // need to pass around to get this example to work. - // In C, this would be a malloc'd or stack-allocated - // struct that we'd cast to a void* and store as the - // data field in our uv_connect_t struct - let req_str_bytes = str::to_bytes(req_str); - let req_msg_ptr: *u8 = vec::raw::to_ptr(req_str_bytes); - debug!("req_msg ptr: %u", req_msg_ptr as uint); - let req_msg = ~[ - buf_init(req_msg_ptr, req_str_bytes.len()) - ]; - // this is the enclosing record, we'll pass a ptr to - // this to C.. - let write_handle = write_t(); - let write_handle_ptr: *uv_write_t = &write_handle; - debug!("tcp req: tcp stream: %d write_handle: %d", - tcp_handle_ptr as int, - write_handle_ptr as int); - let client_data = request_wrapper { - write_req: write_handle_ptr, - req_buf: &req_msg, - read_chan: client_chan - }; - - let tcp_init_result = tcp_init(test_loop as *libc::c_void, - tcp_handle_ptr); - if (tcp_init_result == 0) { - debug!(~"successful tcp_init_result"); - - debug!(~"building addr..."); - let addr = ip4_addr(ip, port); - // FIXME ref #2064 - let addr_ptr: *sockaddr_in = &addr; - debug!("after build addr in rust. port: %u", - addr.sin_port as uint); - - // this should set up the connection request.. - debug!("b4 call tcp_connect connect cb: %u ", - on_connect_cb as uint); - let tcp_connect_result = tcp_connect(connect_req_ptr, - tcp_handle_ptr, - addr_ptr, - on_connect_cb); - if (tcp_connect_result == 0) { - // not set the data on the connect_req - // until its initialized - set_data_for_req(connect_req_ptr as *libc::c_void, - &client_data); - set_data_for_uv_handle(tcp_handle_ptr as *libc::c_void, - &client_data); - debug!(~"before run tcp req loop"); - run(test_loop); - debug!(~"after run tcp req loop"); - } - else { - debug!(~"tcp_connect() failure"); - assert!(false); - } - } - else { - debug!(~"tcp_init() failure"); - assert!(false); - } - loop_delete(test_loop); - } - } - - extern fn server_after_close_cb(handle: *libc::c_void) { - debug!("SERVER server stream closed, should exit. h: %?", - handle); - } - - extern fn client_stream_after_close_cb(handle: *libc::c_void) { - unsafe { - debug!(~"SERVER: closed client stream, now closing server stream"); - let client_data = get_data_for_uv_handle( - handle) as - *tcp_server_data; - close((*client_data).server as *libc::c_void, - server_after_close_cb); - } - } - - extern fn after_server_resp_write(req: *uv_write_t) { - unsafe { - let client_stream_ptr = - get_stream_handle_from_write_req(req); - debug!(~"SERVER: resp sent... closing client stream"); - close(client_stream_ptr as *libc::c_void, - client_stream_after_close_cb) - } - } - - extern fn on_server_read_cb(client_stream_ptr: *uv_stream_t, - nread: libc::ssize_t, - buf: uv_buf_t) { - unsafe { - let nread = nread as int; - if (nread > 0) { - // we have data - debug!("SERVER read: data! nread: %d", nread); - - // pull out the contents of the write from the client - let buf_base = get_base_from_buf(buf); - let buf_len = get_len_from_buf(buf) as uint; - debug!("SERVER buf base: %u, len: %u, nread: %d", - buf_base as uint, - buf_len as uint, - nread); - let bytes = vec::from_buf(buf_base, nread as uint); - let request_str = str::from_bytes(bytes); - - let client_data = get_data_for_uv_handle( - client_stream_ptr as *libc::c_void) as *tcp_server_data; - - let server_kill_msg = copy (*client_data).server_kill_msg; - let write_req = (*client_data).server_write_req; - if str::contains(request_str, server_kill_msg) { - debug!(~"SERVER: client req contains kill_msg!"); - debug!(~"SERVER: sending response to client"); - read_stop(client_stream_ptr); - let server_chan = (*client_data).server_chan.clone(); - server_chan.send(request_str); - let write_result = write( - write_req, - client_stream_ptr as *libc::c_void, - (*client_data).server_resp_buf, - after_server_resp_write); - debug!("SERVER: resp write result: %d", - write_result as int); - if (write_result != 0i32) { - debug!(~"bad result for server resp write()"); - debug!(get_last_err_info( - get_loop_for_uv_handle(client_stream_ptr - as *libc::c_void))); - assert!(false); - } - } - else { - debug!(~"SERVER: client req !contain kill_msg!"); - } - } - else if (nread == -1) { - // err .. possibly EOF - debug!(~"read: eof!"); - } - else { - // nread == 0 .. do nothing, just free buf as below - debug!(~"read: do nothing!"); - } - // when we're done - free_base_of_buf(buf); - debug!(~"SERVER exiting on_read_cb"); - } - } - - extern fn server_connection_cb(server_stream_ptr: - *uv_stream_t, - status: libc::c_int) { - unsafe { - debug!(~"client connecting!"); - let test_loop = get_loop_for_uv_handle( - server_stream_ptr as *libc::c_void); - if status != 0i32 { - let err_msg = get_last_err_info(test_loop); - debug!("server_connect_cb: non-zero status: %?", - err_msg); - return; - } - let server_data = get_data_for_uv_handle( - server_stream_ptr as *libc::c_void) as *tcp_server_data; - let client_stream_ptr = (*server_data).client; - let client_init_result = tcp_init(test_loop, - client_stream_ptr); - set_data_for_uv_handle( - client_stream_ptr as *libc::c_void, - server_data as *libc::c_void); - if (client_init_result == 0i32) { - debug!(~"successfully initialized client stream"); - let accept_result = accept(server_stream_ptr as - *libc::c_void, - client_stream_ptr as - *libc::c_void); - if (accept_result == 0i32) { - // start reading - let read_result = read_start( - client_stream_ptr as *uv_stream_t, - on_alloc_cb, - on_server_read_cb); - if (read_result == 0i32) { - debug!(~"successful server read start"); - } - else { - debug!("server_connection_cb: bad read:%d", - read_result as int); - assert!(false); - } - } - else { - debug!("server_connection_cb: bad accept: %d", - accept_result as int); - assert!(false); - } - } - else { - debug!("server_connection_cb: bad client init: %d", - client_init_result as int); - assert!(false); - } - } - } - - struct tcp_server_data { - client: *uv_tcp_t, - server: *uv_tcp_t, - server_kill_msg: ~str, - server_resp_buf: *~[uv_buf_t], - server_chan: SharedChan<~str>, - server_write_req: *uv_write_t, - } - - struct async_handle_data { - continue_chan: SharedChan<bool>, - } - - extern fn async_close_cb(handle: *libc::c_void) { - debug!("SERVER: closing async cb... h: %?", - handle); - } - - extern fn continue_async_cb(async_handle: *uv_async_t, - status: libc::c_int) { - unsafe { - // once we're in the body of this callback, - // the tcp server's loop is set up, so we - // can continue on to let the tcp client - // do its thang - let data = get_data_for_uv_handle( - async_handle as *libc::c_void) as *async_handle_data; - let continue_chan = (*data).continue_chan.clone(); - let should_continue = status == 0i32; - continue_chan.send(should_continue); - close(async_handle as *libc::c_void, async_close_cb); - } - } - - fn impl_uv_tcp_server(server_ip: &str, - server_port: int, - kill_server_msg: ~str, - server_resp_msg: ~str, - server_chan: SharedChan<~str>, - continue_chan: SharedChan<bool>) { - unsafe { - let test_loop = loop_new(); - let tcp_server = tcp_t(); - let tcp_server_ptr: *uv_tcp_t = &tcp_server; - - let tcp_client = tcp_t(); - let tcp_client_ptr: *uv_tcp_t = &tcp_client; - - let server_write_req = write_t(); - let server_write_req_ptr: *uv_write_t = &server_write_req; - - let resp_str_bytes = str::to_bytes(server_resp_msg); - let resp_msg_ptr: *u8 = vec::raw::to_ptr(resp_str_bytes); - debug!("resp_msg ptr: %u", resp_msg_ptr as uint); - let resp_msg = ~[ - buf_init(resp_msg_ptr, resp_str_bytes.len()) - ]; - - let continue_async_handle = async_t(); - let continue_async_handle_ptr: *uv_async_t = - &continue_async_handle; - let async_data = - async_handle_data { continue_chan: continue_chan }; - let async_data_ptr: *async_handle_data = &async_data; - - let server_data = tcp_server_data { - client: tcp_client_ptr, - server: tcp_server_ptr, - server_kill_msg: kill_server_msg, - server_resp_buf: &resp_msg, - server_chan: server_chan, - server_write_req: server_write_req_ptr - }; - let server_data_ptr: *tcp_server_data = &server_data; - set_data_for_uv_handle(tcp_server_ptr as *libc::c_void, - server_data_ptr as *libc::c_void); - - // uv_tcp_init() - let tcp_init_result = tcp_init( - test_loop as *libc::c_void, tcp_server_ptr); - if (tcp_init_result == 0i32) { - let server_addr = ip4_addr(server_ip, server_port); - // FIXME ref #2064 - let server_addr_ptr: *sockaddr_in = &server_addr; - - // uv_tcp_bind() - let bind_result = tcp_bind(tcp_server_ptr, server_addr_ptr); - if (bind_result == 0i32) { - debug!(~"successful uv_tcp_bind, listening"); - - // uv_listen() - let listen_result = listen(tcp_server_ptr as - *libc::c_void, - 128i32, - server_connection_cb); - if (listen_result == 0i32) { - // let the test know it can set up the tcp server, - // now.. this may still present a race, not sure.. - let async_result = async_init(test_loop, - continue_async_handle_ptr, - continue_async_cb); - if (async_result == 0i32) { - set_data_for_uv_handle( - continue_async_handle_ptr as *libc::c_void, - async_data_ptr as *libc::c_void); - async_send(continue_async_handle_ptr); - // uv_run() - run(test_loop); - debug!(~"server uv::run() has returned"); - } - else { - debug!("uv_async_init failure: %d", - async_result as int); - assert!(false); - } - } - else { - debug!("non-zero result on uv_listen: %d", - listen_result as int); - assert!(false); - } - } - else { - debug!("non-zero result on uv_tcp_bind: %d", - bind_result as int); - assert!(false); - } - } - else { - debug!("non-zero result on uv_tcp_init: %d", - tcp_init_result as int); - assert!(false); - } - loop_delete(test_loop); - } - } - - // this is the impl for a test that is (maybe) ran on a - // per-platform/arch basis below - pub fn impl_uv_tcp_server_and_request() { - let bind_ip = ~"0.0.0.0"; - let request_ip = ~"127.0.0.1"; - let port = 8886; - let kill_server_msg = ~"does a dog have buddha nature?"; - let server_resp_msg = ~"mu!"; - let (client_port, client_chan) = stream::<~str>(); - let client_chan = SharedChan::new(client_chan); - let (server_port, server_chan) = stream::<~str>(); - let server_chan = SharedChan::new(server_chan); - - let (continue_port, continue_chan) = stream::<bool>(); - let continue_chan = SharedChan::new(continue_chan); - - let kill_server_msg_copy = copy kill_server_msg; - let server_resp_msg_copy = copy server_resp_msg; - do task::spawn_sched(task::ManualThreads(1)) { - impl_uv_tcp_server(bind_ip, port, - copy kill_server_msg_copy, - copy server_resp_msg_copy, - server_chan.clone(), - continue_chan.clone()); - }; - - // block until the server up is.. possibly a race? - debug!(~"before receiving on server continue_port"); - continue_port.recv(); - debug!(~"received on continue port, set up tcp client"); - - let kill_server_msg_copy = copy kill_server_msg; - do task::spawn_sched(task::ManualThreads(1u)) { - impl_uv_tcp_request(request_ip, port, - kill_server_msg_copy, - client_chan.clone()); - }; - - let msg_from_client = server_port.recv(); - let msg_from_server = client_port.recv(); - - assert!(str::contains(msg_from_client, kill_server_msg)); - assert!(str::contains(msg_from_server, server_resp_msg)); - } - - // FIXME don't run on fbsd or linux 32 bit(#2064) - #[cfg(target_os="win32")] - #[cfg(target_os="darwin")] - #[cfg(target_os="linux")] - #[cfg(target_os="android")] - mod tcp_and_server_client_test { - #[cfg(target_arch="x86_64")] - mod impl64 { - #[test] - fn test_uv_ll_tcp_server_and_request() { - unsafe { - super::super::impl_uv_tcp_server_and_request(); - } - } - } - #[cfg(target_arch="x86")] - #[cfg(target_arch="arm")] - #[cfg(target_arch="mips")] - mod impl32 { - #[test] - #[ignore(cfg(target_os = "linux"))] - fn test_uv_ll_tcp_server_and_request() { - unsafe { - super::super::impl_uv_tcp_server_and_request(); - } - } - } - } - - fn struct_size_check_common<TStruct>(t_name: ~str, - foreign_size: libc::c_uint) { - let rust_size = sys::size_of::<TStruct>(); - let sizes_match = foreign_size as uint == rust_size; - if !sizes_match { - let output = fmt!( - "STRUCT_SIZE FAILURE: %s -- actual: %u expected: %u", - t_name, rust_size, foreign_size as uint); - debug!(output); - } - assert!(sizes_match); - } - - // struct size tests - #[test] - fn test_uv_ll_struct_size_uv_tcp_t() { - unsafe { - struct_size_check_common::<uv_tcp_t>( - ~"uv_tcp_t", - super::rust_uv_helper_uv_tcp_t_size() - ); - } - } - #[test] - fn test_uv_ll_struct_size_uv_connect_t() { - unsafe { - struct_size_check_common::<uv_connect_t>( - ~"uv_connect_t", - super::rust_uv_helper_uv_connect_t_size() - ); - } - } - #[test] - fn test_uv_ll_struct_size_uv_buf_t() { - unsafe { - struct_size_check_common::<uv_buf_t>( - ~"uv_buf_t", - super::rust_uv_helper_uv_buf_t_size() - ); - } - } - #[test] - fn test_uv_ll_struct_size_uv_write_t() { - unsafe { - struct_size_check_common::<uv_write_t>( - ~"uv_write_t", - super::rust_uv_helper_uv_write_t_size() - ); - } - } - - #[test] - fn test_uv_ll_struct_size_sockaddr_in() { - unsafe { - struct_size_check_common::<sockaddr_in>( - ~"sockaddr_in", - super::rust_uv_helper_sockaddr_in_size() - ); - } - } - #[test] - fn test_uv_ll_struct_size_sockaddr_in6() { - unsafe { - let foreign_handle_size = - super::rust_uv_helper_sockaddr_in6_size(); - let rust_handle_size = sys::size_of::<sockaddr_in6>(); - let output = fmt!("sockaddr_in6 -- foreign: %u rust: %u", - foreign_handle_size as uint, rust_handle_size); - debug!(output); - // FIXME #1645 .. rust appears to pad structs to the nearest - // byte..? - // .. can't get the uv::ll::sockaddr_in6 to == 28 :/ - // .. so the type always appears to be 32 in size.. which is - // good, i guess.. better too big than too little - assert!((4u+foreign_handle_size as uint) == - rust_handle_size); - } - } - #[test] - #[ignore(reason = "questionable size calculations")] - fn test_uv_ll_struct_size_addr_in() { - unsafe { - let foreign_handle_size = - super::rust_uv_helper_addr_in_size(); - let rust_handle_size = sys::size_of::<addr_in>(); - let output = fmt!("addr_in -- foreign: %u rust: %u", - foreign_handle_size as uint, rust_handle_size); - debug!(output); - // FIXME #1645 .. see note above about struct padding - assert!((4u+foreign_handle_size as uint) == - rust_handle_size); - } - } - - #[test] - fn test_uv_ll_struct_size_uv_async_t() { - unsafe { - struct_size_check_common::<uv_async_t>( - ~"uv_async_t", - super::rust_uv_helper_uv_async_t_size() - ); - } - } - - #[test] - fn test_uv_ll_struct_size_uv_timer_t() { - unsafe { - struct_size_check_common::<uv_timer_t>( - ~"uv_timer_t", - super::rust_uv_helper_uv_timer_t_size() - ); - } - } - - #[test] - #[ignore(cfg(target_os = "win32"))] - fn test_uv_ll_struct_size_uv_getaddrinfo_t() { - unsafe { - struct_size_check_common::<uv_getaddrinfo_t>( - ~"uv_getaddrinfo_t", - super::rust_uv_helper_uv_getaddrinfo_t_size() - ); - } - } - #[test] - #[ignore(cfg(target_os = "macos"))] - #[ignore(cfg(target_os = "win32"))] - fn test_uv_ll_struct_size_addrinfo() { - unsafe { - struct_size_check_common::<uv_timer_t>( - ~"addrinfo", - super::rust_uv_helper_uv_timer_t_size() - ); - } - } -} diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs new file mode 100644 index 00000000000..65f8dab25a5 --- /dev/null +++ b/src/libstd/vec.rs @@ -0,0 +1,4615 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// 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. + +//! Vectors + +#[warn(non_camel_case_types)]; + +use cast::transmute; +use cast; +use container::{Container, Mutable}; +use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater}; +use clone::Clone; +use old_iter::BaseIter; +use old_iter; +use iterator::Iterator; +use kinds::Copy; +use libc; +use old_iter::CopyableIter; +use option::{None, Option, Some}; +use ptr::to_unsafe_ptr; +use ptr; +use ptr::Ptr; +use sys; +use uint; +use unstable::intrinsics; +use vec; +use util; + +#[cfg(not(test))] use cmp::Equiv; + +pub mod rustrt { + use libc; + use sys; + use vec::raw; + + #[abi = "cdecl"] + pub extern { + // These names are terrible. reserve_shared applies + // to ~[] and reserve_shared_actual applies to @[]. + #[fast_ffi] + unsafe fn vec_reserve_shared(t: *sys::TypeDesc, + v: **raw::VecRepr, + n: libc::size_t); + #[fast_ffi] + unsafe fn vec_reserve_shared_actual(t: *sys::TypeDesc, + v: **raw::VecRepr, + n: libc::size_t); + } +} + +/// Returns true if a vector contains no elements +pub fn is_empty<T>(v: &const [T]) -> bool { + as_const_buf(v, |_p, len| len == 0u) +} + +/// Returns true if two vectors have the same length +pub fn same_length<T, U>(xs: &const [T], ys: &const [U]) -> bool { + xs.len() == ys.len() +} + +/** + * Reserves capacity for exactly `n` elements in the given vector. + * + * If the capacity for `v` is already equal to or greater than the requested + * capacity, then no action is taken. + * + * # Arguments + * + * * v - A vector + * * n - The number of elements to reserve space for + */ +#[inline] +pub fn reserve<T>(v: &mut ~[T], n: uint) { + // Only make the (slow) call into the runtime if we have to + use managed; + if capacity(v) < n { + unsafe { + let ptr: **raw::VecRepr = cast::transmute(v); + let td = sys::get_type_desc::<T>(); + if ((**ptr).box_header.ref_count == + managed::raw::RC_MANAGED_UNIQUE) { + rustrt::vec_reserve_shared_actual(td, ptr, n as libc::size_t); + } else { + rustrt::vec_reserve_shared(td, ptr, n as libc::size_t); + } + } + } +} + +/** + * Reserves capacity for at least `n` elements in the given vector. + * + * This function will over-allocate in order to amortize the allocation costs + * in scenarios where the caller may need to repeatedly reserve additional + * space. + * + * If the capacity for `v` is already equal to or greater than the requested + * capacity, then no action is taken. + * + * # Arguments + * + * * v - A vector + * * n - The number of elements to reserve space for + */ +pub fn reserve_at_least<T>(v: &mut ~[T], n: uint) { + reserve(v, uint::next_power_of_two(n)); +} + +/// Returns the number of elements the vector can hold without reallocating +#[inline(always)] +pub fn capacity<T>(v: &const ~[T]) -> uint { + unsafe { + let repr: **raw::VecRepr = transmute(v); + (**repr).unboxed.alloc / sys::nonzero_size_of::<T>() + } +} + +/// Returns the length of a vector +#[inline(always)] +pub fn len<T>(v: &const [T]) -> uint { + as_const_buf(v, |_p, len| len) +} + +// A botch to tide us over until core and std are fully demuted. +pub fn uniq_len<T>(v: &const ~[T]) -> uint { + unsafe { + let v: &~[T] = transmute(v); + as_const_buf(*v, |_p, len| len) + } +} + +/** + * Creates and initializes an owned vector. + * + * Creates an owned vector of size `n_elts` and initializes the elements + * to the value returned by the function `op`. + */ +pub fn from_fn<T>(n_elts: uint, op: old_iter::InitOp<T>) -> ~[T] { + unsafe { + let mut v = with_capacity(n_elts); + do as_mut_buf(v) |p, _len| { + let mut i: uint = 0u; + while i < n_elts { + intrinsics::move_val_init(&mut(*ptr::mut_offset(p, i)), + op(i)); + i += 1u; + } + } + raw::set_len(&mut v, n_elts); + v + } +} + +/** + * Creates and initializes an owned vector. + * + * Creates an owned vector of size `n_elts` and initializes the elements + * to the value `t`. + */ +pub fn from_elem<T:Copy>(n_elts: uint, t: T) -> ~[T] { + from_fn(n_elts, |_i| copy t) +} + +/// Creates a new unique vector with the same contents as the slice +pub fn to_owned<T:Copy>(t: &[T]) -> ~[T] { + from_fn(t.len(), |i| t[i]) +} + +/// Creates a new vector with a capacity of `capacity` +pub fn with_capacity<T>(capacity: uint) -> ~[T] { + let mut vec = ~[]; + reserve(&mut vec, capacity); + vec +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * This version takes an initial capacity for the vector. + * + * # Arguments + * + * * size - An initial size of the vector to reserve + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build_sized<A>(size: uint, builder: &fn(push: &fn(v: A))) -> ~[A] { + let mut vec = with_capacity(size); + builder(|x| vec.push(x)); + vec +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * + * # Arguments + * + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build<A>(builder: &fn(push: &fn(v: A))) -> ~[A] { + build_sized(4, builder) +} + +/** + * Builds a vector by calling a provided function with an argument + * function that pushes an element to the back of a vector. + * This version takes an initial size for the vector. + * + * # Arguments + * + * * size - An option, maybe containing initial size of the vector to reserve + * * builder - A function that will construct the vector. It receives + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pub fn build_sized_opt<A>(size: Option<uint>, + builder: &fn(push: &fn(v: A))) + -> ~[A] { + build_sized(size.get_or_default(4), builder) +} + +// Accessors + +/// Returns the first element of a vector +pub fn head<'r,T>(v: &'r [T]) -> &'r T { + if v.len() == 0 { fail!("head: empty vector") } + &v[0] +} + +/// Returns `Some(x)` where `x` is the first element of the slice `v`, +/// or `None` if the vector is empty. +pub fn head_opt<'r,T>(v: &'r [T]) -> Option<&'r T> { + if v.len() == 0 { None } else { Some(&v[0]) } +} + +/// Returns a vector containing all but the first element of a slice +pub fn tail<'r,T>(v: &'r [T]) -> &'r [T] { slice(v, 1, v.len()) } + +/// Returns a vector containing all but the first `n` elements of a slice +pub fn tailn<'r,T>(v: &'r [T], n: uint) -> &'r [T] { slice(v, n, v.len()) } + +/// Returns a vector containing all but the last element of a slice +pub fn init<'r,T>(v: &'r [T]) -> &'r [T] { slice(v, 0, v.len() - 1) } + +/// Returns a vector containing all but the last `n' elements of a slice +pub fn initn<'r,T>(v: &'r [T], n: uint) -> &'r [T] { + slice(v, 0, v.len() - n) +} + +/// Returns the last element of the slice `v`, failing if the slice is empty. +pub fn last<'r,T>(v: &'r [T]) -> &'r T { + if v.len() == 0 { fail!("last: empty vector") } + &v[v.len() - 1] +} + +/// Returns `Some(x)` where `x` is the last element of the slice `v`, or +/// `None` if the vector is empty. +pub fn last_opt<'r,T>(v: &'r [T]) -> Option<&'r T> { + if v.len() == 0 { None } else { Some(&v[v.len() - 1]) } +} + +/// Return a slice that points into another slice. +#[inline(always)] +pub fn slice<'r,T>(v: &'r [T], start: uint, end: uint) -> &'r [T] { + assert!(start <= end); + assert!(end <= len(v)); + do as_imm_buf(v) |p, _len| { + unsafe { + transmute((ptr::offset(p, start), + (end - start) * sys::nonzero_size_of::<T>())) + } + } +} + +/// Return a slice that points into another slice. +#[inline(always)] +pub fn mut_slice<'r,T>(v: &'r mut [T], start: uint, end: uint) + -> &'r mut [T] { + assert!(start <= end); + assert!(end <= v.len()); + do as_mut_buf(v) |p, _len| { + unsafe { + transmute((ptr::mut_offset(p, start), + (end - start) * sys::nonzero_size_of::<T>())) + } + } +} + +/// Return a slice that points into another slice. +#[inline(always)] +pub fn const_slice<'r,T>(v: &'r const [T], start: uint, end: uint) + -> &'r const [T] { + assert!(start <= end); + assert!(end <= len(v)); + do as_const_buf(v) |p, _len| { + unsafe { + transmute((ptr::const_offset(p, start), + (end - start) * sys::nonzero_size_of::<T>())) + } + } +} + +/// Copies + +/// Split the vector `v` by applying each element against the predicate `f`. +pub fn split<T:Copy>(v: &[T], f: &fn(t: &T) -> bool) -> ~[~[T]] { + let ln = len(v); + if (ln == 0u) { return ~[] } + + let mut start = 0u; + let mut result = ~[]; + while start < ln { + match position_between(v, start, ln, f) { + None => break, + Some(i) => { + result.push(slice(v, start, i).to_vec()); + start = i + 1u; + } + } + } + result.push(slice(v, start, ln).to_vec()); + result +} + +/** + * Split the vector `v` by applying each element against the predicate `f` up + * to `n` times. + */ +pub fn splitn<T:Copy>(v: &[T], n: uint, f: &fn(t: &T) -> bool) -> ~[~[T]] { + let ln = len(v); + if (ln == 0u) { return ~[] } + + let mut start = 0u; + let mut count = n; + let mut result = ~[]; + while start < ln && count > 0u { + match position_between(v, start, ln, f) { + None => break, + Some(i) => { + result.push(slice(v, start, i).to_vec()); + // Make sure to skip the separator. + start = i + 1u; + count -= 1u; + } + } + } + result.push(slice(v, start, ln).to_vec()); + result +} + +/** + * Reverse split the vector `v` by applying each element against the predicate + * `f`. + */ +pub fn rsplit<T:Copy>(v: &[T], f: &fn(t: &T) -> bool) -> ~[~[T]] { + let ln = len(v); + if (ln == 0) { return ~[] } + + let mut end = ln; + let mut result = ~[]; + while end > 0 { + match rposition_between(v, 0, end, f) { + None => break, + Some(i) => { + result.push(slice(v, i + 1, end).to_vec()); + end = i; + } + } + } + result.push(slice(v, 0u, end).to_vec()); + reverse(result); + result +} + +/** + * Reverse split the vector `v` by applying each element against the predicate + * `f` up to `n times. + */ +pub fn rsplitn<T:Copy>(v: &[T], n: uint, f: &fn(t: &T) -> bool) -> ~[~[T]] { + let ln = len(v); + if (ln == 0u) { return ~[] } + + let mut end = ln; + let mut count = n; + let mut result = ~[]; + while end > 0u && count > 0u { + match rposition_between(v, 0u, end, f) { + None => break, + Some(i) => { + result.push(slice(v, i + 1u, end).to_vec()); + // Make sure to skip the separator. + end = i; + count -= 1u; + } + } + } + result.push(slice(v, 0u, end).to_vec()); + reverse(result); + result +} + +/** + * Partitions a vector into two new vectors: those that satisfies the + * predicate, and those that do not. + */ +pub fn partition<T>(v: ~[T], f: &fn(&T) -> bool) -> (~[T], ~[T]) { + let mut lefts = ~[]; + let mut rights = ~[]; + + // FIXME (#4355 maybe): using v.consume here crashes + // do v.consume |_, elt| { + do consume(v) |_, elt| { + if f(&elt) { + lefts.push(elt); + } else { + rights.push(elt); + } + } + + (lefts, rights) +} + +/** + * Partitions a vector into two new vectors: those that satisfies the + * predicate, and those that do not. + */ +pub fn partitioned<T:Copy>(v: &[T], f: &fn(&T) -> bool) -> (~[T], ~[T]) { + let mut lefts = ~[]; + let mut rights = ~[]; + + for each(v) |elt| { + if f(elt) { + lefts.push(*elt); + } else { + rights.push(*elt); + } + } + + (lefts, rights) +} + +// Mutators + +/// Removes the first element from a vector and return it +pub fn shift<T>(v: &mut ~[T]) -> T { + unsafe { + assert!(!v.is_empty()); + + if v.len() == 1 { return v.pop() } + + if v.len() == 2 { + let last = v.pop(); + let first = v.pop(); + v.push(last); + return first; + } + + let ln = v.len(); + let next_ln = v.len() - 1; + + // Save the last element. We're going to overwrite its position + let work_elt = v.pop(); + // We still should have room to work where what last element was + assert!(capacity(v) >= ln); + // Pretend like we have the original length so we can use + // the vector copy_memory to overwrite the hole we just made + raw::set_len(&mut *v, ln); + + // Memcopy the head element (the one we want) to the location we just + // popped. For the moment it unsafely exists at both the head and last + // positions + { + let first_slice = slice(*v, 0, 1); + let last_slice = slice(*v, next_ln, ln); + raw::copy_memory(transmute(last_slice), first_slice, 1); + } + + // Memcopy everything to the left one element + { + let init_slice = slice(*v, 0, next_ln); + let tail_slice = slice(*v, 1, ln); + raw::copy_memory(transmute(init_slice), + tail_slice, + next_ln); + } + + // Set the new length. Now the vector is back to normal + raw::set_len(&mut *v, next_ln); + + // Swap out the element we want from the end + let vp = raw::to_mut_ptr(*v); + let vp = ptr::mut_offset(vp, next_ln - 1); + + util::replace_ptr(vp, work_elt) + } +} + +/// Prepend an element to the vector +pub fn unshift<T>(v: &mut ~[T], x: T) { + let vv = util::replace(v, ~[x]); + v.push_all_move(vv); +} + +/// Insert an element at position i within v, shifting all +/// elements after position i one position to the right. +pub fn insert<T>(v: &mut ~[T], i: uint, x: T) { + let len = v.len(); + assert!(i <= len); + + v.push(x); + let mut j = len; + while j > i { + swap(*v, j, j - 1); + j -= 1; + } +} + +/// Remove and return the element at position i within v, shifting +/// all elements after position i one position to the left. +pub fn remove<T>(v: &mut ~[T], i: uint) -> T { + let len = v.len(); + assert!(i < len); + + let mut j = i; + while j < len - 1 { + swap(*v, j, j + 1); + j += 1; + } + v.pop() +} + +pub fn consume<T>(mut v: ~[T], f: &fn(uint, v: T)) { + unsafe { + do as_mut_buf(v) |p, ln| { + for uint::range(0, ln) |i| { + // NB: This unsafe operation counts on init writing 0s to the + // holes we create in the vector. That ensures that, if the + // iterator fails then we won't try to clean up the consumed + // elements during unwinding + let x = intrinsics::init(); + let p = ptr::mut_offset(p, i); + f(i, util::replace_ptr(p, x)); + } + } + + raw::set_len(&mut v, 0); + } +} + +pub fn consume_reverse<T>(mut v: ~[T], f: &fn(uint, v: T)) { + unsafe { + do as_mut_buf(v) |p, ln| { + let mut i = ln; + while i > 0 { + i -= 1; + + // NB: This unsafe operation counts on init writing 0s to the + // holes we create in the vector. That ensures that, if the + // iterator fails then we won't try to clean up the consumed + // elements during unwinding + let x = intrinsics::init(); + let p = ptr::mut_offset(p, i); + f(i, util::replace_ptr(p, x)); + } + } + + raw::set_len(&mut v, 0); + } +} + +/// Remove the last element from a vector and return it +pub fn pop<T>(v: &mut ~[T]) -> T { + let ln = v.len(); + if ln == 0 { + fail!("sorry, cannot vec::pop an empty vector") + } + let valptr = ptr::to_mut_unsafe_ptr(&mut v[ln - 1u]); + unsafe { + let val = util::replace_ptr(valptr, intrinsics::init()); + raw::set_len(v, ln - 1u); + val + } +} + +/** + * Remove an element from anywhere in the vector and return it, replacing it + * with the last element. This does not preserve ordering, but is O(1). + * + * Fails if index >= length. + */ +pub fn swap_remove<T>(v: &mut ~[T], index: uint) -> T { + let ln = v.len(); + if index >= ln { + fail!("vec::swap_remove - index %u >= length %u", index, ln); + } + if index < ln - 1 { + swap(*v, index, ln - 1); + } + v.pop() +} + +/// Append an element to a vector +#[inline(always)] +pub fn push<T>(v: &mut ~[T], initval: T) { + unsafe { + let repr: **raw::VecRepr = transmute(&mut *v); + let fill = (**repr).unboxed.fill; + if (**repr).unboxed.alloc > fill { + push_fast(v, initval); + } + else { + push_slow(v, initval); + } + } +} + +// This doesn't bother to make sure we have space. +#[inline(always)] // really pretty please +unsafe fn push_fast<T>(v: &mut ~[T], initval: T) { + let repr: **mut raw::VecRepr = transmute(v); + let fill = (**repr).unboxed.fill; + (**repr).unboxed.fill += sys::nonzero_size_of::<T>(); + let p = to_unsafe_ptr(&((**repr).unboxed.data)); + let p = ptr::offset(p, fill) as *mut T; + intrinsics::move_val_init(&mut(*p), initval); +} + +#[inline(never)] +fn push_slow<T>(v: &mut ~[T], initval: T) { + let new_len = v.len() + 1; + reserve_at_least(&mut *v, new_len); + unsafe { push_fast(v, initval) } +} + +#[inline(always)] +pub fn push_all<T:Copy>(v: &mut ~[T], rhs: &const [T]) { + let new_len = v.len() + rhs.len(); + reserve(&mut *v, new_len); + + for uint::range(0u, rhs.len()) |i| { + push(&mut *v, unsafe { raw::get(rhs, i) }) + } +} + +#[inline(always)] +pub fn push_all_move<T>(v: &mut ~[T], mut rhs: ~[T]) { + let new_len = v.len() + rhs.len(); + reserve(&mut *v, new_len); + unsafe { + do as_mut_buf(rhs) |p, len| { + for uint::range(0, len) |i| { + let x = util::replace_ptr(ptr::mut_offset(p, i), + intrinsics::uninit()); + push(&mut *v, x); + } + } + raw::set_len(&mut rhs, 0); + } +} + +/// Shorten a vector, dropping excess elements. +pub fn truncate<T>(v: &mut ~[T], newlen: uint) { + do as_mut_buf(*v) |p, oldlen| { + assert!(newlen <= oldlen); + unsafe { + // This loop is optimized out for non-drop types. + for uint::range(newlen, oldlen) |i| { + util::replace_ptr(ptr::mut_offset(p, i), intrinsics::uninit()); + } + } + } + unsafe { raw::set_len(&mut *v, newlen); } +} + +/** + * Remove consecutive repeated elements from a vector; if the vector is + * sorted, this removes all duplicates. + */ +pub fn dedup<T:Eq>(v: &mut ~[T]) { + unsafe { + if v.len() < 1 { return; } + let mut last_written = 0, next_to_read = 1; + do as_const_buf(*v) |p, ln| { + // We have a mutable reference to v, so we can make arbitrary + // changes. (cf. push and pop) + let p = p as *mut T; + // last_written < next_to_read <= ln + while next_to_read < ln { + // last_written < next_to_read < ln + if *ptr::mut_offset(p, next_to_read) == + *ptr::mut_offset(p, last_written) { + util::replace_ptr(ptr::mut_offset(p, next_to_read), + intrinsics::uninit()); + } else { + last_written += 1; + // last_written <= next_to_read < ln + if next_to_read != last_written { + util::swap_ptr(ptr::mut_offset(p, last_written), + ptr::mut_offset(p, next_to_read)); + } + } + // last_written <= next_to_read < ln + next_to_read += 1; + // last_written < next_to_read <= ln + } + } + // last_written < next_to_read == ln + raw::set_len(v, last_written + 1); + } +} + +// Appending +#[inline(always)] +pub fn append<T:Copy>(lhs: ~[T], rhs: &const [T]) -> ~[T] { + let mut v = lhs; + v.push_all(rhs); + v +} + +#[inline(always)] +pub fn append_one<T>(lhs: ~[T], x: T) -> ~[T] { + let mut v = lhs; + v.push(x); + v +} + +/** + * Expands a vector in place, initializing the new elements to a given value + * + * # Arguments + * + * * v - The vector to grow + * * n - The number of elements to add + * * initval - The value for the new elements + */ +pub fn grow<T:Copy>(v: &mut ~[T], n: uint, initval: &T) { + let new_len = v.len() + n; + reserve_at_least(&mut *v, new_len); + let mut i: uint = 0u; + + while i < n { + v.push(*initval); + i += 1u; + } +} + +/** + * Expands a vector in place, initializing the new elements to the result of + * a function + * + * Function `init_op` is called `n` times with the values [0..`n`) + * + * # Arguments + * + * * v - The vector to grow + * * n - The number of elements to add + * * init_op - A function to call to retreive each appended element's + * value + */ +pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: old_iter::InitOp<T>) { + let new_len = v.len() + n; + reserve_at_least(&mut *v, new_len); + let mut i: uint = 0u; + while i < n { + v.push(op(i)); + i += 1u; + } +} + +/** + * Sets the value of a vector element at a given index, growing the vector as + * needed + * + * Sets the element at position `index` to `val`. If `index` is past the end + * of the vector, expands the vector by replicating `initval` to fill the + * intervening space. + */ +pub fn grow_set<T:Copy>(v: &mut ~[T], index: uint, initval: &T, val: T) { + let l = v.len(); + if index >= l { grow(&mut *v, index - l + 1u, initval); } + v[index] = val; +} + +// Functional utilities + +/// Apply a function to each element of a vector and return the results +pub fn map<T, U>(v: &[T], f: &fn(t: &T) -> U) -> ~[U] { + let mut result = with_capacity(len(v)); + for each(v) |elem| { + result.push(f(elem)); + } + result +} + +pub fn map_consume<T, U>(v: ~[T], f: &fn(v: T) -> U) -> ~[U] { + let mut result = ~[]; + do consume(v) |_i, x| { + result.push(f(x)); + } + result +} + +/// Apply a function to each element of a vector and return the results +pub fn mapi<T, U>(v: &[T], f: &fn(uint, t: &T) -> U) -> ~[U] { + let mut i = 0; + do map(v) |e| { + i += 1; + f(i - 1, e) + } +} + +/** + * Apply a function to each element of a vector and return a concatenation + * of each result vector + */ +pub fn flat_map<T, U>(v: &[T], f: &fn(t: &T) -> ~[U]) -> ~[U] { + let mut result = ~[]; + for each(v) |elem| { result.push_all_move(f(elem)); } + result +} + +/** + * Apply a function to each pair of elements and return the results. + * Equivalent to `map(zip(v0, v1), f)`. + */ +pub fn map_zip<T:Copy,U:Copy,V>(v0: &[T], v1: &[U], + f: &fn(t: &T, v: &U) -> V) -> ~[V] { + let v0_len = len(v0); + if v0_len != len(v1) { fail!(); } + let mut u: ~[V] = ~[]; + let mut i = 0u; + while i < v0_len { + u.push(f(&v0[i], &v1[i])); + i += 1u; + } + u +} + +pub fn filter_map<T, U>( + v: ~[T], + f: &fn(t: T) -> Option<U>) -> ~[U] +{ + /*! + * + * Apply a function to each element of a vector and return the results. + * Consumes the input vector. If function `f` returns `None` then that + * element is excluded from the resulting vector. + */ + + let mut result = ~[]; + do consume(v) |_, elem| { + match f(elem) { + None => {} + Some(result_elem) => { result.push(result_elem); } + } + } + result +} + +pub fn filter_mapped<T, U: Copy>( + v: &[T], + f: &fn(t: &T) -> Option<U>) -> ~[U] +{ + /*! + * + * Like `filter_map()`, but operates on a borrowed slice + * and does not consume the input. + */ + + let mut result = ~[]; + for each(v) |elem| { + match f(elem) { + None => {/* no-op */ } + Some(result_elem) => { result.push(result_elem); } + } + } + result +} + +/** + * Construct a new vector from the elements of a vector for which some + * predicate holds. + * + * Apply function `f` to each element of `v` and return a vector containing + * only those elements for which `f` returned true. + */ +pub fn filter<T>(v: ~[T], f: &fn(t: &T) -> bool) -> ~[T] { + let mut result = ~[]; + // FIXME (#4355 maybe): using v.consume here crashes + // do v.consume |_, elem| { + do consume(v) |_, elem| { + if f(&elem) { result.push(elem); } + } + result +} + +/** + * Construct a new vector from the elements of a vector for which some + * predicate holds. + * + * Apply function `f` to each element of `v` and return a vector containing + * only those elements for which `f` returned true. + */ +pub fn filtered<T:Copy>(v: &[T], f: &fn(t: &T) -> bool) -> ~[T] { + let mut result = ~[]; + for each(v) |elem| { + if f(elem) { result.push(*elem); } + } + result +} + +/** + * Like `filter()`, but in place. Preserves order of `v`. Linear time. + */ +pub fn retain<T>(v: &mut ~[T], f: &fn(t: &T) -> bool) { + let len = v.len(); + let mut deleted: uint = 0; + + for uint::range(0, len) |i| { + if !f(&v[i]) { + deleted += 1; + } else if deleted > 0 { + swap(*v, i - deleted, i); + } + } + + if deleted > 0 { + v.truncate(len - deleted); + } +} + +/** + * Concatenate a vector of vectors. + * + * Flattens a vector of vectors of T into a single vector of T. + */ +pub fn concat<T:Copy>(v: &[~[T]]) -> ~[T] { + let mut r = ~[]; + for each(v) |inner| { r.push_all(*inner); } + r +} + +/// Concatenate a vector of vectors, placing a given separator between each +pub fn connect<T:Copy>(v: &[~[T]], sep: &T) -> ~[T] { + let mut r: ~[T] = ~[]; + let mut first = true; + for each(v) |inner| { + if first { first = false; } else { r.push(*sep); } + r.push_all(*inner); + } + r +} + +/** + * Reduces a vector from left to right. + * + * # Arguments + * * `z` - initial accumulator value + * * `v` - vector to iterate over + * * `p` - a closure to operate on vector elements + * + * # Examples + * + * Sum all values in the vector [1, 2, 3]: + * + * ~~~ + * vec::foldl(0, [1, 2, 3], |a, b| a + *b); + * ~~~ + * + */ +pub fn foldl<'a, T, U>(z: T, v: &'a [U], p: &fn(t: T, u: &'a U) -> T) -> T { + let mut accum = z; + let mut i = 0; + let l = v.len(); + while i < l { + // Use a while loop so that liveness analysis can handle moving + // the accumulator. + accum = p(accum, &v[i]); + i += 1; + } + accum +} + +/** + * Reduces a vector from right to left. Note that the argument order is + * reversed compared to `foldl` to reflect the order they are provided to + * the closure. + * + * # Arguments + * * `v` - vector to iterate over + * * `z` - initial accumulator value + * * `p` - a closure to do operate on vector elements + * + * # Examples + * + * Sum all values in the vector [1, 2, 3]: + * + * ~~~ + * vec::foldr([1, 2, 3], 0, |a, b| a + *b); + * ~~~ + * + */ +pub fn foldr<'a, T, U>(v: &'a [T], mut z: U, p: &fn(t: &'a T, u: U) -> U) -> U { + let mut i = v.len(); + while i > 0 { + i -= 1; + z = p(&v[i], z); + } + return z; +} + +/** + * Return true if a predicate matches any elements + * + * If the vector contains no elements then false is returned. + */ +pub fn any<T>(v: &[T], f: &fn(t: &T) -> bool) -> bool { + for each(v) |elem| { if f(elem) { return true; } } + false +} + +/** + * Return true if a predicate matches any elements in both vectors. + * + * If the vectors contains no elements then false is returned. + */ +pub fn any2<T, U>(v0: &[T], v1: &[U], + f: &fn(a: &T, b: &U) -> bool) -> bool { + let v0_len = len(v0); + let v1_len = len(v1); + let mut i = 0u; + while i < v0_len && i < v1_len { + if f(&v0[i], &v1[i]) { return true; }; + i += 1u; + } + false +} + +/** + * Return true if a predicate matches all elements + * + * If the vector contains no elements then true is returned. + */ +pub fn all<T>(v: &[T], f: &fn(t: &T) -> bool) -> bool { + for each(v) |elem| { if !f(elem) { return false; } } + true +} + +/** + * Return true if a predicate matches all elements + * + * If the vector contains no elements then true is returned. + */ +pub fn alli<T>(v: &[T], f: &fn(uint, t: &T) -> bool) -> bool { + for eachi(v) |i, elem| { if !f(i, elem) { return false; } } + true +} + +/** + * Return true if a predicate matches all elements in both vectors. + * + * If the vectors are not the same size then false is returned. + */ +pub fn all2<T, U>(v0: &[T], v1: &[U], + f: &fn(t: &T, u: &U) -> bool) -> bool { + let v0_len = len(v0); + if v0_len != len(v1) { return false; } + let mut i = 0u; + while i < v0_len { if !f(&v0[i], &v1[i]) { return false; }; i += 1u; } + true +} + +/// Return true if a vector contains an element with the given value +pub fn contains<T:Eq>(v: &[T], x: &T) -> bool { + for each(v) |elt| { if *x == *elt { return true; } } + false +} + +/// Returns the number of elements that are equal to a given value +pub fn count<T:Eq>(v: &[T], x: &T) -> uint { + let mut cnt = 0u; + for each(v) |elt| { if *x == *elt { cnt += 1u; } } + cnt +} + +/** + * Search for the first element that matches a given predicate + * + * Apply function `f` to each element of `v`, starting from the first. + * When function `f` returns true then an option containing the element + * is returned. If `f` matches no elements then none is returned. + */ +pub fn find<T:Copy>(v: &[T], f: &fn(t: &T) -> bool) -> Option<T> { + find_between(v, 0u, len(v), f) +} + +/** + * Search for the first element that matches a given predicate within a range + * + * Apply function `f` to each element of `v` within the range + * [`start`, `end`). When function `f` returns true then an option containing + * the element is returned. If `f` matches no elements then none is returned. + */ +pub fn find_between<T:Copy>(v: &[T], start: uint, end: uint, + f: &fn(t: &T) -> bool) -> Option<T> { + position_between(v, start, end, f).map(|i| v[*i]) +} + +/** + * Search for the last element that matches a given predicate + * + * Apply function `f` to each element of `v` in reverse order. When function + * `f` returns true then an option containing the element is returned. If `f` + * matches no elements then none is returned. + */ +pub fn rfind<T:Copy>(v: &[T], f: &fn(t: &T) -> bool) -> Option<T> { + rfind_between(v, 0u, len(v), f) +} + +/** + * Search for the last element that matches a given predicate within a range + * + * Apply function `f` to each element of `v` in reverse order within the range + * [`start`, `end`). When function `f` returns true then an option containing + * the element is returned. If `f` matches no elements then none is return. + */ +pub fn rfind_between<T:Copy>(v: &[T], + start: uint, + end: uint, + f: &fn(t: &T) -> bool) + -> Option<T> { + rposition_between(v, start, end, f).map(|i| v[*i]) +} + +/// Find the first index containing a matching value +pub fn position_elem<T:Eq>(v: &[T], x: &T) -> Option<uint> { + position(v, |y| *x == *y) +} + +/** + * Find the first index matching some predicate + * + * Apply function `f` to each element of `v`. When function `f` returns true + * then an option containing the index is returned. If `f` matches no elements + * then none is returned. + */ +pub fn position<T>(v: &[T], f: &fn(t: &T) -> bool) -> Option<uint> { + position_between(v, 0u, len(v), f) +} + +/** + * Find the first index matching some predicate within a range + * + * Apply function `f` to each element of `v` between the range + * [`start`, `end`). When function `f` returns true then an option containing + * the index is returned. If `f` matches no elements then none is returned. + */ +pub fn position_between<T>(v: &[T], + start: uint, + end: uint, + f: &fn(t: &T) -> bool) + -> Option<uint> { + assert!(start <= end); + assert!(end <= len(v)); + let mut i = start; + while i < end { if f(&v[i]) { return Some::<uint>(i); } i += 1u; } + None +} + +/// Find the last index containing a matching value +pub fn rposition_elem<T:Eq>(v: &[T], x: &T) -> Option<uint> { + rposition(v, |y| *x == *y) +} + +/** + * Find the last index matching some predicate + * + * Apply function `f` to each element of `v` in reverse order. When function + * `f` returns true then an option containing the index is returned. If `f` + * matches no elements then none is returned. + */ +pub fn rposition<T>(v: &[T], f: &fn(t: &T) -> bool) -> Option<uint> { + rposition_between(v, 0u, len(v), f) +} + +/** + * Find the last index matching some predicate within a range + * + * Apply function `f` to each element of `v` in reverse order between the + * range [`start`, `end`). When function `f` returns true then an option + * containing the index is returned. If `f` matches no elements then none is + * returned. + */ +pub fn rposition_between<T>(v: &[T], start: uint, end: uint, + f: &fn(t: &T) -> bool) -> Option<uint> { + assert!(start <= end); + assert!(end <= len(v)); + let mut i = end; + while i > start { + if f(&v[i - 1u]) { return Some::<uint>(i - 1u); } + i -= 1u; + } + None +} + + + +/** + * Binary search a sorted vector with a comparator function. + * + * The comparator should implement an order consistent with the sort + * order of the underlying vector, returning an order code that indicates + * whether its argument is `Less`, `Equal` or `Greater` the desired target. + * + * Returns the index where the comparator returned `Equal`, or `None` if + * not found. + */ +pub fn bsearch<T>(v: &[T], f: &fn(&T) -> Ordering) -> Option<uint> { + let mut base : uint = 0; + let mut lim : uint = v.len(); + + while lim != 0 { + let ix = base + (lim >> 1); + match f(&v[ix]) { + Equal => return Some(ix), + Less => { + base = ix + 1; + lim -= 1; + } + Greater => () + } + lim >>= 1; + } + return None; +} + +/** + * Binary search a sorted vector for a given element. + * + * Returns the index of the element or None if not found. + */ +pub fn bsearch_elem<T:TotalOrd>(v: &[T], x: &T) -> Option<uint> { + bsearch(v, |p| p.cmp(x)) +} + +// FIXME: if issue #586 gets implemented, could have a postcondition +// saying the two result lists have the same length -- or, could +// return a nominal record with a constraint saying that, instead of +// returning a tuple (contingent on issue #869) +/** + * Convert a vector of pairs into a pair of vectors, by reference. As unzip(). + */ +pub fn unzip_slice<T:Copy,U:Copy>(v: &[(T, U)]) -> (~[T], ~[U]) { + let mut ts = ~[], us = ~[]; + for each(v) |p| { + let (t, u) = *p; + ts.push(t); + us.push(u); + } + (ts, us) +} + +/** + * Convert a vector of pairs into a pair of vectors. + * + * Returns a tuple containing two vectors where the i-th element of the first + * vector contains the first element of the i-th tuple of the input vector, + * and the i-th element of the second vector contains the second element + * of the i-th tuple of the input vector. + */ +pub fn unzip<T,U>(v: ~[(T, U)]) -> (~[T], ~[U]) { + let mut ts = ~[], us = ~[]; + do consume(v) |_i, p| { + let (t, u) = p; + ts.push(t); + us.push(u); + } + (ts, us) +} + +/** + * Convert two vectors to a vector of pairs, by reference. As zip(). + */ +pub fn zip_slice<T:Copy,U:Copy>(v: &const [T], u: &const [U]) + -> ~[(T, U)] { + let mut zipped = ~[]; + let sz = len(v); + let mut i = 0u; + assert_eq!(sz, len(u)); + while i < sz { + zipped.push((v[i], u[i])); + i += 1u; + } + zipped +} + +/** + * Convert two vectors to a vector of pairs. + * + * Returns a vector of tuples, where the i-th tuple contains contains the + * i-th elements from each of the input vectors. + */ +pub fn zip<T, U>(mut v: ~[T], mut u: ~[U]) -> ~[(T, U)] { + let mut i = len(v); + assert_eq!(i, len(u)); + let mut w = with_capacity(i); + while i > 0 { + w.push((v.pop(),u.pop())); + i -= 1; + } + reverse(w); + w +} + +/** + * Swaps two elements in a vector + * + * # Arguments + * + * * v The input vector + * * a - The index of the first element + * * b - The index of the second element + */ +#[inline(always)] +pub fn swap<T>(v: &mut [T], a: uint, b: uint) { + unsafe { + // Can't take two mutable loans from one vector, so instead just cast + // them to their raw pointers to do the swap + let pa: *mut T = ptr::to_mut_unsafe_ptr(&mut v[a]); + let pb: *mut T = ptr::to_mut_unsafe_ptr(&mut v[b]); + util::swap_ptr(pa, pb); + } +} + +/// Reverse the order of elements in a vector, in place +pub fn reverse<T>(v: &mut [T]) { + let mut i: uint = 0; + let ln = len::<T>(v); + while i < ln / 2 { + swap(v, i, ln - i - 1); + i += 1; + } +} + +/** + * Reverse part of a vector in place. + * + * Reverse the elements in the vector between `start` and `end - 1`. + * + * If either start or end do not represent valid positions in the vector, the + * vector is returned unchanged. + * + * # Arguments + * + * * `v` - The mutable vector to be modified + * + * * `start` - Index of the first element of the slice + * + * * `end` - Index one past the final element to be reversed. + * + * # Example + * + * Assume a mutable vector `v` contains `[1,2,3,4,5]`. After the call: + * + * ~~~ + * + * reverse_part(v, 1, 4); + * + * ~~~ + * + * `v` now contains `[1,4,3,2,5]`. + */ +pub fn reverse_part<T>(v: &mut [T], start: uint, end : uint) { + let sz = v.len(); + if start >= sz || end > sz { return; } + let mut i = start; + let mut j = end - 1; + while i < j { + vec::swap(v, i, j); + i += 1; + j -= 1; + } +} + +/// Returns a vector with the order of elements reversed +pub fn reversed<T:Copy>(v: &const [T]) -> ~[T] { + let mut rs: ~[T] = ~[]; + let mut i = len::<T>(v); + if i == 0 { return (rs); } else { i -= 1; } + while i != 0 { rs.push(v[i]); i -= 1; } + rs.push(v[0]); + rs +} + +/** + * Iterates over a vector, yielding each element to a closure. + * + * # Arguments + * + * * `v` - A vector, to be iterated over + * * `f` - A closure to do the iterating. Within this closure, return true to + * * continue iterating, false to break. + * + * # Examples + * ~~~ + * [1,2,3].each(|&i| { + * io::println(int::str(i)); + * true + * }); + * ~~~ + * + * ~~~ + * [1,2,3,4,5].each(|&i| { + * if i < 4 { + * io::println(int::str(i)); + * true + * } + * else { + * false + * } + * }); + * ~~~ + * + * You probably will want to use each with a `for`/`do` expression, depending + * on your iteration needs: + * + * ~~~ + * for [1,2,3].each |&i| { + * io::println(int::str(i)); + * } + * ~~~ + */ +#[inline(always)] +pub fn _each<'r,T>(v: &'r [T], f: &fn(&'r T) -> bool) -> bool { + // ^^^^ + // NB---this CANNOT be &const [T]! The reason + // is that you are passing it to `f()` using + // an immutable. + + let mut broke = false; + do as_imm_buf(v) |p, n| { + let mut n = n; + let mut p = p; + while n > 0u { + unsafe { + let q = cast::copy_lifetime_vec(v, &*p); + if !f(q) { break; } + p = ptr::offset(p, 1u); + } + n -= 1u; + } + broke = n > 0; + } + return true; +} + +pub fn each<'r,T>(v: &'r [T], f: &fn(&'r T) -> bool) -> bool { _each(v, f) } + +/// Like `each()`, but for the case where you have +/// a vector with mutable contents and you would like +/// to mutate the contents as you iterate. +#[inline(always)] +pub fn _each_mut<'r,T>(v: &'r mut [T], f: &fn(elem: &'r mut T) -> bool) -> bool { + let mut broke = false; + do as_mut_buf(v) |p, n| { + let mut n = n; + let mut p = p; + while n > 0 { + unsafe { + let q: &'r mut T = cast::transmute_mut_region(&mut *p); + if !f(q) { break; } + p = p.offset(1); + } + n -= 1; + } + broke = n > 0; + } + return broke; +} + +pub fn each_mut<'r,T>(v: &'r mut [T], f: &fn(elem: &'r mut T) -> bool) -> bool { + _each_mut(v, f) +} + +/// Like `each()`, but for the case where you have a vector that *may or may +/// not* have mutable contents. +#[inline(always)] +pub fn _each_const<T>(v: &const [T], f: &fn(elem: &const T) -> bool) -> bool { + let mut i = 0; + let n = v.len(); + while i < n { + if !f(&const v[i]) { + return false; + } + i += 1; + } + return true; +} + +pub fn each_const<t>(v: &const [t], f: &fn(elem: &const t) -> bool) -> bool { + _each_const(v, f) +} + +/** + * Iterates over a vector's elements and indices + * + * Return true to continue, false to break. + */ +#[inline(always)] +pub fn _eachi<'r,T>(v: &'r [T], f: &fn(uint, v: &'r T) -> bool) -> bool { + let mut i = 0; + for each(v) |p| { + if !f(i, p) { return false; } + i += 1; + } + return true; +} + +pub fn eachi<'r,T>(v: &'r [T], f: &fn(uint, v: &'r T) -> bool) -> bool { + _eachi(v, f) +} + +/** + * Iterates over a mutable vector's elements and indices + * + * Return true to continue, false to break. + */ +#[inline(always)] +pub fn _eachi_mut<'r,T>(v: &'r mut [T], + f: &fn(uint, v: &'r mut T) -> bool) -> bool { + let mut i = 0; + for each_mut(v) |p| { + if !f(i, p) { + return false; + } + i += 1; + } + return true; +} + +pub fn eachi_mut<'r,T>(v: &'r mut [T], + f: &fn(uint, v: &'r mut T) -> bool) -> bool { + _eachi_mut(v, f) +} + +/** + * Iterates over a vector's elements in reverse + * + * Return true to continue, false to break. + */ +#[inline(always)] +pub fn _each_reverse<'r,T>(v: &'r [T], blk: &fn(v: &'r T) -> bool) -> bool { + _eachi_reverse(v, |_i, v| blk(v)) +} + +pub fn each_reverse<'r,T>(v: &'r [T], blk: &fn(v: &'r T) -> bool) -> bool { + _each_reverse(v, blk) +} + +/** + * Iterates over a vector's elements and indices in reverse + * + * Return true to continue, false to break. + */ +#[inline(always)] +pub fn _eachi_reverse<'r,T>(v: &'r [T], + blk: &fn(i: uint, v: &'r T) -> bool) -> bool { + let mut i = v.len(); + while i > 0 { + i -= 1; + if !blk(i, &v[i]) { + return false; + } + } + return true; +} + +pub fn eachi_reverse<'r,T>(v: &'r [T], + blk: &fn(i: uint, v: &'r T) -> bool) -> bool { + _eachi_reverse(v, blk) +} + +/** + * Iterates over two vectors simultaneously + * + * # Failure + * + * Both vectors must have the same length + */ +#[inline] +pub fn _each2<U, T>(v1: &[U], v2: &[T], f: &fn(u: &U, t: &T) -> bool) -> bool { + assert_eq!(v1.len(), v2.len()); + for uint::range(0u, v1.len()) |i| { + if !f(&v1[i], &v2[i]) { + return false; + } + } + return true; +} + +pub fn each2<U, T>(v1: &[U], v2: &[T], f: &fn(u: &U, t: &T) -> bool) -> bool { + _each2(v1, v2, f) +} + +/** + * + * Iterates over two vector with mutable. + * + * # Failure + * + * Both vectors must have the same length + */ +#[inline] +pub fn _each2_mut<U, T>(v1: &mut [U], v2: &mut [T], f: &fn(u: &mut U, t: &mut T) -> bool) -> bool { + assert_eq!(v1.len(), v2.len()); + for uint::range(0u, v1.len()) |i| { + if !f(&mut v1[i], &mut v2[i]) { + return false; + } + } + return true; +} + +pub fn each2_mut<U, T>(v1: &mut [U], v2: &mut [T], f: &fn(u: &mut U, t: &mut T) -> bool) -> bool { + _each2_mut(v1, v2, f) +} + +/** + * Iterate over all permutations of vector `v`. + * + * Permutations are produced in lexicographic order with respect to the order + * of elements in `v` (so if `v` is sorted then the permutations are + * lexicographically sorted). + * + * The total number of permutations produced is `len(v)!`. If `v` contains + * repeated elements, then some permutations are repeated. + * + * See [Algorithms to generate + * permutations](http://en.wikipedia.org/wiki/Permutation). + * + * # Arguments + * + * * `values` - A vector of values from which the permutations are + * chosen + * + * * `fun` - The function to iterate over the combinations + */ +pub fn each_permutation<T:Copy>(values: &[T], fun: &fn(perm : &[T]) -> bool) -> bool { + let length = values.len(); + let mut permutation = vec::from_fn(length, |i| values[i]); + if length <= 1 { + fun(permutation); + return true; + } + let mut indices = vec::from_fn(length, |i| i); + loop { + if !fun(permutation) { return true; } + // find largest k such that indices[k] < indices[k+1] + // if no such k exists, all permutations have been generated + let mut k = length - 2; + while k > 0 && indices[k] >= indices[k+1] { + k -= 1; + } + if k == 0 && indices[0] > indices[1] { return true; } + // find largest l such that indices[k] < indices[l] + // k+1 is guaranteed to be such + let mut l = length - 1; + while indices[k] >= indices[l] { + l -= 1; + } + // swap indices[k] and indices[l]; sort indices[k+1..] + // (they're just reversed) + vec::swap(indices, k, l); + reverse_part(indices, k+1, length); + // fixup permutation based on indices + for uint::range(k, length) |i| { + permutation[i] = values[indices[i]]; + } + } +} + +/** + * Iterate over all contiguous windows of length `n` of the vector `v`. + * + * # Example + * + * Print the adjacent pairs of a vector (i.e. `[1,2]`, `[2,3]`, `[3,4]`) + * + * ~~~ + * for windowed(2, &[1,2,3,4]) |v| { + * io::println(fmt!("%?", v)); + * } + * ~~~ + * + */ +pub fn windowed<'r, T>(n: uint, v: &'r [T], it: &fn(&'r [T]) -> bool) -> bool { + assert!(1u <= n); + if n > v.len() { return true; } + for uint::range(0, v.len() - n + 1) |i| { + if !it(v.slice(i, i + n)) { return false; } + } + return true; +} + +/** + * Work with the buffer of a vector. + * + * Allows for unsafe manipulation of vector contents, which is useful for + * foreign interop. + */ +#[inline(always)] +pub fn as_imm_buf<T,U>(s: &[T], + /* NB---this CANNOT be const, see below */ + f: &fn(*T, uint) -> U) -> U { + + // NB---Do not change the type of s to `&const [T]`. This is + // unsound. The reason is that we are going to create immutable pointers + // into `s` and pass them to `f()`, but in fact they are potentially + // pointing at *mutable memory*. Use `as_const_buf` or `as_mut_buf` + // instead! + + unsafe { + let v : *(*T,uint) = transmute(&s); + let (buf,len) = *v; + f(buf, len / sys::nonzero_size_of::<T>()) + } +} + +/// Similar to `as_imm_buf` but passing a `*const T` +#[inline(always)] +pub fn as_const_buf<T,U>(s: &const [T], f: &fn(*const T, uint) -> U) -> U { + unsafe { + let v : *(*const T,uint) = transmute(&s); + let (buf,len) = *v; + f(buf, len / sys::nonzero_size_of::<T>()) + } +} + +/// Similar to `as_imm_buf` but passing a `*mut T` +#[inline(always)] +pub fn as_mut_buf<T,U>(s: &mut [T], f: &fn(*mut T, uint) -> U) -> U { + unsafe { + let v : *(*mut T,uint) = transmute(&s); + let (buf,len) = *v; + f(buf, len / sys::nonzero_size_of::<T>()) + } +} + +// Equality + +fn eq<T: Eq>(a: &[T], b: &[T]) -> bool { + let (a_len, b_len) = (a.len(), b.len()); + if a_len != b_len { return false; } + + let mut i = 0; + while i < a_len { + if a[i] != b[i] { return false; } + i += 1; + } + true +} + +fn equals<T: TotalEq>(a: &[T], b: &[T]) -> bool { + let (a_len, b_len) = (a.len(), b.len()); + if a_len != b_len { return false; } + + let mut i = 0; + while i < a_len { + if !a[i].equals(&b[i]) { return false; } + i += 1; + } + true +} + +#[cfg(not(test))] +impl<'self,T:Eq> Eq for &'self [T] { + #[inline(always)] + fn eq(&self, other: & &'self [T]) -> bool { eq(*self, *other) } + #[inline(always)] + fn ne(&self, other: & &'self [T]) -> bool { !self.eq(other) } +} + +#[cfg(not(test))] +impl<T:Eq> Eq for ~[T] { + #[inline(always)] + fn eq(&self, other: &~[T]) -> bool { eq(*self, *other) } + #[inline(always)] + fn ne(&self, other: &~[T]) -> bool { !self.eq(other) } +} + +#[cfg(not(test))] +impl<T:Eq> Eq for @[T] { + #[inline(always)] + fn eq(&self, other: &@[T]) -> bool { eq(*self, *other) } + #[inline(always)] + fn ne(&self, other: &@[T]) -> bool { !self.eq(other) } +} + +#[cfg(not(test))] +impl<'self,T:TotalEq> TotalEq for &'self [T] { + #[inline(always)] + fn equals(&self, other: & &'self [T]) -> bool { equals(*self, *other) } +} + +#[cfg(not(test))] +impl<T:TotalEq> TotalEq for ~[T] { + #[inline(always)] + fn equals(&self, other: &~[T]) -> bool { equals(*self, *other) } +} + +#[cfg(not(test))] +impl<T:TotalEq> TotalEq for @[T] { + #[inline(always)] + fn equals(&self, other: &@[T]) -> bool { equals(*self, *other) } +} + +#[cfg(not(test))] +impl<'self,T:Eq> Equiv<~[T]> for &'self [T] { + #[inline(always)] + fn equiv(&self, other: &~[T]) -> bool { eq(*self, *other) } +} + +// Lexicographical comparison + +fn cmp<T: TotalOrd>(a: &[T], b: &[T]) -> Ordering { + let low = uint::min(a.len(), b.len()); + + for uint::range(0, low) |idx| { + match a[idx].cmp(&b[idx]) { + Greater => return Greater, + Less => return Less, + Equal => () + } + } + + a.len().cmp(&b.len()) +} + +#[cfg(not(test))] +impl<'self,T:TotalOrd> TotalOrd for &'self [T] { + #[inline(always)] + fn cmp(&self, other: & &'self [T]) -> Ordering { cmp(*self, *other) } +} + +#[cfg(not(test))] +impl<T: TotalOrd> TotalOrd for ~[T] { + #[inline(always)] + fn cmp(&self, other: &~[T]) -> Ordering { cmp(*self, *other) } +} + +#[cfg(not(test))] +impl<T: TotalOrd> TotalOrd for @[T] { + #[inline(always)] + fn cmp(&self, other: &@[T]) -> Ordering { cmp(*self, *other) } +} + +fn lt<T:Ord>(a: &[T], b: &[T]) -> bool { + let (a_len, b_len) = (a.len(), b.len()); + let end = uint::min(a_len, b_len); + + let mut i = 0; + while i < end { + let (c_a, c_b) = (&a[i], &b[i]); + if *c_a < *c_b { return true; } + if *c_a > *c_b { return false; } + i += 1; + } + + a_len < b_len +} + +fn le<T:Ord>(a: &[T], b: &[T]) -> bool { !lt(b, a) } +fn ge<T:Ord>(a: &[T], b: &[T]) -> bool { !lt(a, b) } +fn gt<T:Ord>(a: &[T], b: &[T]) -> bool { lt(b, a) } + +#[cfg(not(test))] +impl<'self,T:Ord> Ord for &'self [T] { + #[inline(always)] + fn lt(&self, other: & &'self [T]) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: & &'self [T]) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: & &'self [T]) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: & &'self [T]) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +impl<T:Ord> Ord for ~[T] { + #[inline(always)] + fn lt(&self, other: &~[T]) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: &~[T]) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: &~[T]) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: &~[T]) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +impl<T:Ord> Ord for @[T] { + #[inline(always)] + fn lt(&self, other: &@[T]) -> bool { lt((*self), (*other)) } + #[inline(always)] + fn le(&self, other: &@[T]) -> bool { le((*self), (*other)) } + #[inline(always)] + fn ge(&self, other: &@[T]) -> bool { ge((*self), (*other)) } + #[inline(always)] + fn gt(&self, other: &@[T]) -> bool { gt((*self), (*other)) } +} + +#[cfg(not(test))] +pub mod traits { + use kinds::Copy; + use ops::Add; + use vec::append; + + impl<'self,T:Copy> Add<&'self const [T],~[T]> for ~[T] { + #[inline(always)] + fn add(&self, rhs: & &'self const [T]) -> ~[T] { + append(copy *self, (*rhs)) + } + } +} + +impl<'self,T> Container for &'self const [T] { + /// Returns true if a vector contains no elements + #[inline] + fn is_empty(&const self) -> bool { is_empty(*self) } + + /// Returns the length of a vector + #[inline] + fn len(&const self) -> uint { len(*self) } +} + +pub trait CopyableVector<T> { + fn to_owned(&self) -> ~[T]; +} + +/// Extension methods for vectors +impl<'self,T:Copy> CopyableVector<T> for &'self [T] { + /// Returns a copy of `v`. + #[inline] + fn to_owned(&self) -> ~[T] { + let mut result = ~[]; + reserve(&mut result, self.len()); + for self.each |e| { + result.push(copy *e); + } + result + + } +} + +pub trait ImmutableVector<'self, T> { + fn slice(&self, start: uint, end: uint) -> &'self [T]; + fn iter(self) -> VecIterator<'self, T>; + fn head(&self) -> &'self T; + fn head_opt(&self) -> Option<&'self T>; + fn tail(&self) -> &'self [T]; + fn tailn(&self, n: uint) -> &'self [T]; + fn init(&self) -> &'self [T]; + fn initn(&self, n: uint) -> &'self [T]; + fn last(&self) -> &'self T; + fn last_opt(&self) -> Option<&'self T>; + fn position(&self, f: &fn(t: &T) -> bool) -> Option<uint>; + fn rposition(&self, f: &fn(t: &T) -> bool) -> Option<uint>; + fn each_reverse(&self, blk: &fn(&T) -> bool) -> bool; + fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool) -> bool; + fn foldr<'a, U>(&'a self, z: U, p: &fn(t: &'a T, u: U) -> U) -> U; + fn map<U>(&self, f: &fn(t: &T) -> U) -> ~[U]; + fn mapi<U>(&self, f: &fn(uint, t: &T) -> U) -> ~[U]; + fn map_r<U>(&self, f: &fn(x: &T) -> U) -> ~[U]; + fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool; + fn flat_map<U>(&self, f: &fn(t: &T) -> ~[U]) -> ~[U]; + fn filter_mapped<U:Copy>(&self, f: &fn(t: &T) -> Option<U>) -> ~[U]; + unsafe fn unsafe_ref(&self, index: uint) -> *T; +} + +/// Extension methods for vectors +impl<'self,T> ImmutableVector<'self, T> for &'self [T] { + /// Return a slice that points into another slice. + #[inline] + fn slice(&self, start: uint, end: uint) -> &'self [T] { + slice(*self, start, end) + } + + #[inline] + fn iter(self) -> VecIterator<'self, T> { + unsafe { + let p = vec::raw::to_ptr(self); + VecIterator{ptr: p, end: p.offset(self.len()), + lifetime: cast::transmute(p)} + } + } + + /// Returns the first element of a vector, failing if the vector is empty. + #[inline] + fn head(&self) -> &'self T { head(*self) } + + /// Returns the first element of a vector + #[inline] + fn head_opt(&self) -> Option<&'self T> { head_opt(*self) } + + /// Returns all but the first element of a vector + #[inline] + fn tail(&self) -> &'self [T] { tail(*self) } + + /// Returns all but the first `n' elements of a vector + #[inline] + fn tailn(&self, n: uint) -> &'self [T] { tailn(*self, n) } + + /// Returns all but the last elemnt of a vector + #[inline] + fn init(&self) -> &'self [T] { init(*self) } + + /// Returns all but the last `n' elemnts of a vector + #[inline] + fn initn(&self, n: uint) -> &'self [T] { initn(*self, n) } + + /// Returns the last element of a `v`, failing if the vector is empty. + #[inline] + fn last(&self) -> &'self T { last(*self) } + + /// Returns the last element of a `v`, failing if the vector is empty. + #[inline] + fn last_opt(&self) -> Option<&'self T> { last_opt(*self) } + + /** + * Find the first index matching some predicate + * + * Apply function `f` to each element of `v`. When function `f` returns + * true then an option containing the index is returned. If `f` matches no + * elements then none is returned. + */ + #[inline] + fn position(&self, f: &fn(t: &T) -> bool) -> Option<uint> { + position(*self, f) + } + + /** + * Find the last index matching some predicate + * + * Apply function `f` to each element of `v` in reverse order. When + * function `f` returns true then an option containing the index is + * returned. If `f` matches no elements then none is returned. + */ + #[inline] + fn rposition(&self, f: &fn(t: &T) -> bool) -> Option<uint> { + rposition(*self, f) + } + + /// Iterates over a vector's elements in reverse. + #[inline] + fn each_reverse(&self, blk: &fn(&T) -> bool) -> bool { + each_reverse(*self, blk) + } + + /// Iterates over a vector's elements and indices in reverse. + #[inline] + fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool) -> bool { + eachi_reverse(*self, blk) + } + + /// Reduce a vector from right to left + #[inline] + fn foldr<'a, U>(&'a self, z: U, p: &fn(t: &'a T, u: U) -> U) -> U { + foldr(*self, z, p) + } + + /// Apply a function to each element of a vector and return the results + #[inline] + fn map<U>(&self, f: &fn(t: &T) -> U) -> ~[U] { map(*self, f) } + + /** + * Apply a function to the index and value of each element in the vector + * and return the results + */ + fn mapi<U>(&self, f: &fn(uint, t: &T) -> U) -> ~[U] { + mapi(*self, f) + } + + #[inline] + fn map_r<U>(&self, f: &fn(x: &T) -> U) -> ~[U] { + let mut r = ~[]; + let mut i = 0; + while i < self.len() { + r.push(f(&self[i])); + i += 1; + } + r + } + + /** + * Returns true if the function returns true for all elements. + * + * If the vector is empty, true is returned. + */ + fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool { + alli(*self, f) + } + /** + * Apply a function to each element of a vector and return a concatenation + * of each result vector + */ + #[inline] + fn flat_map<U>(&self, f: &fn(t: &T) -> ~[U]) -> ~[U] { + flat_map(*self, f) + } + /** + * Apply a function to each element of a vector and return the results + * + * If function `f` returns `none` then that element is excluded from + * the resulting vector. + */ + #[inline] + fn filter_mapped<U:Copy>(&self, f: &fn(t: &T) -> Option<U>) -> ~[U] { + filter_mapped(*self, f) + } + + /// Returns a pointer to the element at the given index, without doing + /// bounds checking. + #[inline(always)] + unsafe fn unsafe_ref(&self, index: uint) -> *T { + let (ptr, _): (*T, uint) = transmute(*self); + ptr.offset(index) + } +} + +pub trait ImmutableEqVector<T:Eq> { + fn position_elem(&self, t: &T) -> Option<uint>; + fn rposition_elem(&self, t: &T) -> Option<uint>; +} + +impl<'self,T:Eq> ImmutableEqVector<T> for &'self [T] { + /// Find the first index containing a matching value + #[inline] + fn position_elem(&self, x: &T) -> Option<uint> { + position_elem(*self, x) + } + + /// Find the last index containing a matching value + #[inline] + fn rposition_elem(&self, t: &T) -> Option<uint> { + rposition_elem(*self, t) + } +} + +pub trait ImmutableCopyableVector<T> { + fn filtered(&self, f: &fn(&T) -> bool) -> ~[T]; + fn rfind(&self, f: &fn(t: &T) -> bool) -> Option<T>; + fn partitioned(&self, f: &fn(&T) -> bool) -> (~[T], ~[T]); + unsafe fn unsafe_get(&self, elem: uint) -> T; +} + +/// Extension methods for vectors +impl<'self,T:Copy> ImmutableCopyableVector<T> for &'self [T] { + /** + * Construct a new vector from the elements of a vector for which some + * predicate holds. + * + * Apply function `f` to each element of `v` and return a vector + * containing only those elements for which `f` returned true. + */ + #[inline] + fn filtered(&self, f: &fn(t: &T) -> bool) -> ~[T] { + filtered(*self, f) + } + + /** + * Search for the last element that matches a given predicate + * + * Apply function `f` to each element of `v` in reverse order. When + * function `f` returns true then an option containing the element is + * returned. If `f` matches no elements then none is returned. + */ + #[inline] + fn rfind(&self, f: &fn(t: &T) -> bool) -> Option<T> { + rfind(*self, f) + } + + /** + * Partitions the vector into those that satisfies the predicate, and + * those that do not. + */ + #[inline] + fn partitioned(&self, f: &fn(&T) -> bool) -> (~[T], ~[T]) { + partitioned(*self, f) + } + + /// Returns the element at the given index, without doing bounds checking. + #[inline(always)] + unsafe fn unsafe_get(&self, index: uint) -> T { + *self.unsafe_ref(index) + } +} + +pub trait OwnedVector<T> { + fn push(&mut self, t: T); + fn push_all_move(&mut self, rhs: ~[T]); + fn pop(&mut self) -> T; + fn shift(&mut self) -> T; + fn unshift(&mut self, x: T); + fn insert(&mut self, i: uint, x:T); + fn remove(&mut self, i: uint) -> T; + fn swap_remove(&mut self, index: uint) -> T; + fn truncate(&mut self, newlen: uint); + fn retain(&mut self, f: &fn(t: &T) -> bool); + fn consume(self, f: &fn(uint, v: T)); + fn consume_reverse(self, f: &fn(uint, v: T)); + fn filter(self, f: &fn(t: &T) -> bool) -> ~[T]; + fn partition(self, f: &fn(&T) -> bool) -> (~[T], ~[T]); + fn grow_fn(&mut self, n: uint, op: old_iter::InitOp<T>); +} + +impl<T> OwnedVector<T> for ~[T] { + #[inline] + fn push(&mut self, t: T) { + push(self, t); + } + + #[inline] + fn push_all_move(&mut self, rhs: ~[T]) { + push_all_move(self, rhs); + } + + #[inline] + fn pop(&mut self) -> T { + pop(self) + } + + #[inline] + fn shift(&mut self) -> T { + shift(self) + } + + #[inline] + fn unshift(&mut self, x: T) { + unshift(self, x) + } + + #[inline] + fn insert(&mut self, i: uint, x:T) { + insert(self, i, x) + } + + #[inline] + fn remove(&mut self, i: uint) -> T { + remove(self, i) + } + + #[inline] + fn swap_remove(&mut self, index: uint) -> T { + swap_remove(self, index) + } + + #[inline] + fn truncate(&mut self, newlen: uint) { + truncate(self, newlen); + } + + #[inline] + fn retain(&mut self, f: &fn(t: &T) -> bool) { + retain(self, f); + } + + #[inline] + fn consume(self, f: &fn(uint, v: T)) { + consume(self, f) + } + + #[inline] + fn consume_reverse(self, f: &fn(uint, v: T)) { + consume_reverse(self, f) + } + + #[inline] + fn filter(self, f: &fn(&T) -> bool) -> ~[T] { + filter(self, f) + } + + /** + * Partitions the vector into those that satisfies the predicate, and + * those that do not. + */ + #[inline] + fn partition(self, f: &fn(&T) -> bool) -> (~[T], ~[T]) { + partition(self, f) + } + + #[inline] + fn grow_fn(&mut self, n: uint, op: old_iter::InitOp<T>) { + grow_fn(self, n, op); + } +} + +impl<T> Mutable for ~[T] { + /// Clear the vector, removing all values. + fn clear(&mut self) { self.truncate(0) } +} + +pub trait OwnedCopyableVector<T:Copy> { + fn push_all(&mut self, rhs: &const [T]); + fn grow(&mut self, n: uint, initval: &T); + fn grow_set(&mut self, index: uint, initval: &T, val: T); +} + +impl<T:Copy> OwnedCopyableVector<T> for ~[T] { + #[inline] + fn push_all(&mut self, rhs: &const [T]) { + push_all(self, rhs); + } + + #[inline] + fn grow(&mut self, n: uint, initval: &T) { + grow(self, n, initval); + } + + #[inline] + fn grow_set(&mut self, index: uint, initval: &T, val: T) { + grow_set(self, index, initval, val); + } +} + +trait OwnedEqVector<T:Eq> { + fn dedup(&mut self); +} + +impl<T:Eq> OwnedEqVector<T> for ~[T] { + #[inline] + fn dedup(&mut self) { + dedup(self) + } +} + +pub trait MutableVector<T> { + unsafe fn unsafe_mut_ref(&self, index: uint) -> *mut T; + unsafe fn unsafe_set(&self, index: uint, val: T); +} + +impl<'self,T> MutableVector<T> for &'self mut [T] { + #[inline(always)] + unsafe fn unsafe_mut_ref(&self, index: uint) -> *mut T { + let pair_ptr: &(*mut T, uint) = transmute(self); + let (ptr, _) = *pair_ptr; + ptr.offset(index) + } + + #[inline(always)] + unsafe fn unsafe_set(&self, index: uint, val: T) { + *self.unsafe_mut_ref(index) = val; + } +} + +/** +* Constructs a vector from an unsafe pointer to a buffer +* +* # Arguments +* +* * ptr - An unsafe pointer to a buffer of `T` +* * elts - The number of elements in the buffer +*/ +// Wrapper for fn in raw: needs to be called by net_tcp::on_tcp_read_cb +pub unsafe fn from_buf<T>(ptr: *T, elts: uint) -> ~[T] { + raw::from_buf_raw(ptr, elts) +} + +/// The internal 'unboxed' representation of a vector +pub struct UnboxedVecRepr { + fill: uint, + alloc: uint, + data: u8 +} + +/// Unsafe operations +pub mod raw { + use cast::transmute; + use kinds::Copy; + use managed; + use option::{None, Some}; + use ptr; + use sys; + use unstable::intrinsics; + use vec::{UnboxedVecRepr, as_const_buf, as_mut_buf, len, with_capacity}; + use util; + + /// The internal representation of a (boxed) vector + pub struct VecRepr { + box_header: managed::raw::BoxHeaderRepr, + unboxed: UnboxedVecRepr + } + + pub struct SliceRepr { + data: *u8, + len: uint + } + + /** + * Sets the length of a vector + * + * This will explicitly set the size of the vector, without actually + * modifing its buffers, so it is up to the caller to ensure that + * the vector is actually the specified size. + */ + #[inline(always)] + pub unsafe fn set_len<T>(v: &mut ~[T], new_len: uint) { + let repr: **mut VecRepr = transmute(v); + (**repr).unboxed.fill = new_len * sys::nonzero_size_of::<T>(); + } + + /** + * Returns an unsafe pointer to the vector's buffer + * + * The caller must ensure that the vector outlives the pointer this + * function returns, or else it will end up pointing to garbage. + * + * Modifying the vector may cause its buffer to be reallocated, which + * would also make any pointers to it invalid. + */ + #[inline(always)] + pub fn to_ptr<T>(v: &[T]) -> *T { + unsafe { + let repr: **SliceRepr = transmute(&v); + transmute(&((**repr).data)) + } + } + + /** see `to_ptr()` */ + #[inline(always)] + pub fn to_const_ptr<T>(v: &const [T]) -> *const T { + unsafe { + let repr: **SliceRepr = transmute(&v); + transmute(&((**repr).data)) + } + } + + /** see `to_ptr()` */ + #[inline(always)] + pub fn to_mut_ptr<T>(v: &mut [T]) -> *mut T { + unsafe { + let repr: **SliceRepr = transmute(&v); + transmute(&((**repr).data)) + } + } + + /** + * Form a slice from a pointer and length (as a number of units, + * not bytes). + */ + #[inline(always)] + pub unsafe fn buf_as_slice<T,U>(p: *T, + len: uint, + f: &fn(v: &[T]) -> U) -> U { + let pair = (p, len * sys::nonzero_size_of::<T>()); + let v : *(&'blk [T]) = transmute(&pair); + f(*v) + } + + /** + * Form a slice from a pointer and length (as a number of units, + * not bytes). + */ + #[inline(always)] + pub unsafe fn mut_buf_as_slice<T,U>(p: *mut T, + len: uint, + f: &fn(v: &mut [T]) -> U) -> U { + let pair = (p, len * sys::nonzero_size_of::<T>()); + let v : *(&'blk mut [T]) = transmute(&pair); + f(*v) + } + + /** + * Unchecked vector indexing. + */ + #[inline(always)] + pub unsafe fn get<T:Copy>(v: &const [T], i: uint) -> T { + as_const_buf(v, |p, _len| *ptr::const_offset(p, i)) + } + + /** + * Unchecked vector index assignment. Does not drop the + * old value and hence is only suitable when the vector + * is newly allocated. + */ + #[inline(always)] + pub unsafe fn init_elem<T>(v: &mut [T], i: uint, val: T) { + let mut box = Some(val); + do as_mut_buf(v) |p, _len| { + let box2 = util::replace(&mut box, None); + intrinsics::move_val_init(&mut(*ptr::mut_offset(p, i)), + box2.unwrap()); + } + } + + /** + * Constructs a vector from an unsafe pointer to a buffer + * + * # Arguments + * + * * ptr - An unsafe pointer to a buffer of `T` + * * elts - The number of elements in the buffer + */ + // Was in raw, but needs to be called by net_tcp::on_tcp_read_cb + #[inline(always)] + pub unsafe fn from_buf_raw<T>(ptr: *T, elts: uint) -> ~[T] { + let mut dst = with_capacity(elts); + set_len(&mut dst, elts); + as_mut_buf(dst, |p_dst, _len_dst| ptr::copy_memory(p_dst, ptr, elts)); + dst + } + + /** + * Copies data from one vector to another. + * + * Copies `count` bytes from `src` to `dst`. The source and destination + * may overlap. + */ + #[inline(always)] + pub unsafe fn copy_memory<T>(dst: &mut [T], src: &const [T], + count: uint) { + assert!(dst.len() >= count); + assert!(src.len() >= count); + + do as_mut_buf(dst) |p_dst, _len_dst| { + do as_const_buf(src) |p_src, _len_src| { + ptr::copy_memory(p_dst, p_src, count) + } + } + } +} + +/// Operations on `[u8]` +pub mod bytes { + use libc; + use uint; + use vec::raw; + use vec; + + /// Bytewise string comparison + pub fn memcmp(a: &~[u8], b: &~[u8]) -> int { + let a_len = a.len(); + let b_len = b.len(); + let n = uint::min(a_len, b_len) as libc::size_t; + let r = unsafe { + libc::memcmp(raw::to_ptr(*a) as *libc::c_void, + raw::to_ptr(*b) as *libc::c_void, n) as int + }; + + if r != 0 { r } else { + if a_len == b_len { + 0 + } else if a_len < b_len { + -1 + } else { + 1 + } + } + } + + /// Bytewise less than or equal + pub fn lt(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) < 0 } + + /// Bytewise less than or equal + pub fn le(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) <= 0 } + + /// Bytewise equality + pub fn eq(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) == 0 } + + /// Bytewise inequality + pub fn ne(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) != 0 } + + /// Bytewise greater than or equal + pub fn ge(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) >= 0 } + + /// Bytewise greater than + pub fn gt(a: &~[u8], b: &~[u8]) -> bool { memcmp(a, b) > 0 } + + /** + * Copies data from one vector to another. + * + * Copies `count` bytes from `src` to `dst`. The source and destination + * may overlap. + */ + #[inline(always)] + pub fn copy_memory(dst: &mut [u8], src: &const [u8], count: uint) { + // Bound checks are done at vec::raw::copy_memory. + unsafe { vec::raw::copy_memory(dst, src, count) } + } +} + +// ___________________________________________________________________________ +// ITERATION TRAIT METHODS + +impl<'self,A> old_iter::BaseIter<A> for &'self [A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) -> bool { + each(*self, blk) + } + #[inline(always)] + fn size_hint(&self) -> Option<uint> { Some(self.len()) } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::BaseIter<A> for ~[A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) -> bool { + each(*self, blk) + } + #[inline(always)] + fn size_hint(&self) -> Option<uint> { Some(self.len()) } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::BaseIter<A> for @[A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) -> bool { + each(*self, blk) + } + #[inline(always)] + fn size_hint(&self) -> Option<uint> { Some(self.len()) } +} + +impl<'self,A> old_iter::MutableIter<A> for &'self mut [A] { + #[inline(always)] + fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) -> bool { + each_mut(*self, blk) + } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::MutableIter<A> for ~[A] { + #[inline(always)] + fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) -> bool { + each_mut(*self, blk) + } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::MutableIter<A> for @mut [A] { + #[inline(always)] + fn each_mut(&mut self, blk: &fn(v: &mut A) -> bool) -> bool { + each_mut(*self, blk) + } +} + +impl<'self,A> old_iter::ExtendedIter<A> for &'self [A] { + pub fn eachi(&self, blk: &fn(uint, v: &A) -> bool) -> bool { + old_iter::eachi(self, blk) + } + pub fn all(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::all(self, blk) + } + pub fn any(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::any(self, blk) + } + pub fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B { + old_iter::foldl(self, b0, blk) + } + pub fn position(&self, f: &fn(&A) -> bool) -> Option<uint> { + old_iter::position(self, f) + } + fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B] { + old_iter::map_to_vec(self, op) + } + fn flat_map_to_vec<B,IB:BaseIter<B>>(&self, op: &fn(&A) -> IB) + -> ~[B] { + old_iter::flat_map_to_vec(self, op) + } +} + +impl<'self,A> old_iter::ExtendedMutableIter<A> for &'self mut [A] { + #[inline(always)] + pub fn eachi_mut(&mut self, blk: &fn(uint, v: &mut A) -> bool) -> bool { + eachi_mut(*self, blk) + } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::ExtendedIter<A> for ~[A] { + pub fn eachi(&self, blk: &fn(uint, v: &A) -> bool) -> bool { + old_iter::eachi(self, blk) + } + pub fn all(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::all(self, blk) + } + pub fn any(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::any(self, blk) + } + pub fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B { + old_iter::foldl(self, b0, blk) + } + pub fn position(&self, f: &fn(&A) -> bool) -> Option<uint> { + old_iter::position(self, f) + } + fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B] { + old_iter::map_to_vec(self, op) + } + fn flat_map_to_vec<B,IB:BaseIter<B>>(&self, op: &fn(&A) -> IB) + -> ~[B] { + old_iter::flat_map_to_vec(self, op) + } +} + +// FIXME(#4148): This should be redundant +impl<A> old_iter::ExtendedIter<A> for @[A] { + pub fn eachi(&self, blk: &fn(uint, v: &A) -> bool) -> bool { + old_iter::eachi(self, blk) + } + pub fn all(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::all(self, blk) + } + pub fn any(&self, blk: &fn(&A) -> bool) -> bool { + old_iter::any(self, blk) + } + pub fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B { + old_iter::foldl(self, b0, blk) + } + pub fn position(&self, f: &fn(&A) -> bool) -> Option<uint> { + old_iter::position(self, f) + } + fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B] { + old_iter::map_to_vec(self, op) + } + fn flat_map_to_vec<B,IB:BaseIter<B>>(&self, op: &fn(&A) -> IB) + -> ~[B] { + old_iter::flat_map_to_vec(self, op) + } +} + +impl<'self,A:Eq> old_iter::EqIter<A> for &'self [A] { + pub fn contains(&self, x: &A) -> bool { old_iter::contains(self, x) } + pub fn count(&self, x: &A) -> uint { old_iter::count(self, x) } +} + +// FIXME(#4148): This should be redundant +impl<A:Eq> old_iter::EqIter<A> for ~[A] { + pub fn contains(&self, x: &A) -> bool { old_iter::contains(self, x) } + pub fn count(&self, x: &A) -> uint { old_iter::count(self, x) } +} + +// FIXME(#4148): This should be redundant +impl<A:Eq> old_iter::EqIter<A> for @[A] { + pub fn contains(&self, x: &A) -> bool { old_iter::contains(self, x) } + pub fn count(&self, x: &A) -> uint { old_iter::count(self, x) } +} + +impl<'self,A:Copy> old_iter::CopyableIter<A> for &'self [A] { + fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A] { + old_iter::filter_to_vec(self, pred) + } + fn to_vec(&self) -> ~[A] { old_iter::to_vec(self) } + pub fn find(&self, f: &fn(&A) -> bool) -> Option<A> { + old_iter::find(self, f) + } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy> old_iter::CopyableIter<A> for ~[A] { + fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A] { + old_iter::filter_to_vec(self, pred) + } + fn to_vec(&self) -> ~[A] { old_iter::to_vec(self) } + pub fn find(&self, f: &fn(&A) -> bool) -> Option<A> { + old_iter::find(self, f) + } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy> old_iter::CopyableIter<A> for @[A] { + fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A] { + old_iter::filter_to_vec(self, pred) + } + fn to_vec(&self) -> ~[A] { old_iter::to_vec(self) } + pub fn find(&self, f: &fn(&A) -> bool) -> Option<A> { + old_iter::find(self, f) + } +} + +impl<'self,A:Copy + Ord> old_iter::CopyableOrderedIter<A> for &'self [A] { + fn min(&self) -> A { old_iter::min(self) } + fn max(&self) -> A { old_iter::max(self) } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy + Ord> old_iter::CopyableOrderedIter<A> for ~[A] { + fn min(&self) -> A { old_iter::min(self) } + fn max(&self) -> A { old_iter::max(self) } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy + Ord> old_iter::CopyableOrderedIter<A> for @[A] { + fn min(&self) -> A { old_iter::min(self) } + fn max(&self) -> A { old_iter::max(self) } +} + +impl<'self,A:Copy> old_iter::CopyableNonstrictIter<A> for &'self [A] { + fn each_val(&const self, f: &fn(A) -> bool) -> bool { + let mut i = 0; + while i < self.len() { + if !f(copy self[i]) { return false; } + i += 1; + } + return true; + } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy> old_iter::CopyableNonstrictIter<A> for ~[A] { + fn each_val(&const self, f: &fn(A) -> bool) -> bool { + let mut i = 0; + while i < uniq_len(self) { + if !f(copy self[i]) { return false; } + i += 1; + } + return true; + } +} + +// FIXME(#4148): This should be redundant +impl<A:Copy> old_iter::CopyableNonstrictIter<A> for @[A] { + fn each_val(&const self, f: &fn(A) -> bool) -> bool { + let mut i = 0; + while i < self.len() { + if !f(copy self[i]) { return false; } + i += 1; + } + return true; + } +} + +impl<A:Clone> Clone for ~[A] { + #[inline] + fn clone(&self) -> ~[A] { + self.map(|item| item.clone()) + } +} + +// could be implemented with &[T] with .slice(), but this avoids bounds checks +pub struct VecIterator<'self, T> { + priv ptr: *T, + priv end: *T, + priv lifetime: &'self T // FIXME: #5922 +} + +impl<'self, T> Iterator<&'self T> for VecIterator<'self, T> { + #[inline] + fn next(&mut self) -> Option<&'self T> { + unsafe { + if self.ptr == self.end { + None + } else { + let old = self.ptr; + self.ptr = self.ptr.offset(1); + Some(cast::transmute(old)) + } + } + } +} + +#[cfg(test)] +mod tests { + use option::{None, Option, Some}; + use sys; + use vec::*; + use cmp::*; + + fn square(n: uint) -> uint { n * n } + + fn square_ref(n: &uint) -> uint { square(*n) } + + fn is_three(n: &uint) -> bool { *n == 3u } + + fn is_odd(n: &uint) -> bool { *n % 2u == 1u } + + fn is_equal(x: &uint, y:&uint) -> bool { *x == *y } + + fn square_if_odd_r(n: &uint) -> Option<uint> { + if *n % 2u == 1u { Some(*n * *n) } else { None } + } + + fn square_if_odd_v(n: uint) -> Option<uint> { + if n % 2u == 1u { Some(n * n) } else { None } + } + + fn add(x: uint, y: &uint) -> uint { x + *y } + + #[test] + fn test_unsafe_ptrs() { + unsafe { + // Test on-stack copy-from-buf. + let a = ~[1, 2, 3]; + let mut ptr = raw::to_ptr(a); + let b = from_buf(ptr, 3u); + assert_eq!(b.len(), 3u); + assert_eq!(b[0], 1); + assert_eq!(b[1], 2); + assert_eq!(b[2], 3); + + // Test on-heap copy-from-buf. + let c = ~[1, 2, 3, 4, 5]; + ptr = raw::to_ptr(c); + let d = from_buf(ptr, 5u); + assert_eq!(d.len(), 5u); + assert_eq!(d[0], 1); + assert_eq!(d[1], 2); + assert_eq!(d[2], 3); + assert_eq!(d[3], 4); + assert_eq!(d[4], 5); + } + } + + #[test] + fn test_from_fn() { + // Test on-stack from_fn. + let mut v = from_fn(3u, square); + assert_eq!(v.len(), 3u); + assert_eq!(v[0], 0u); + assert_eq!(v[1], 1u); + assert_eq!(v[2], 4u); + + // Test on-heap from_fn. + v = from_fn(5u, square); + assert_eq!(v.len(), 5u); + assert_eq!(v[0], 0u); + assert_eq!(v[1], 1u); + assert_eq!(v[2], 4u); + assert_eq!(v[3], 9u); + assert_eq!(v[4], 16u); + } + + #[test] + fn test_from_elem() { + // Test on-stack from_elem. + let mut v = from_elem(2u, 10u); + assert_eq!(v.len(), 2u); + assert_eq!(v[0], 10u); + assert_eq!(v[1], 10u); + + // Test on-heap from_elem. + v = from_elem(6u, 20u); + assert_eq!(v[0], 20u); + assert_eq!(v[1], 20u); + assert_eq!(v[2], 20u); + assert_eq!(v[3], 20u); + assert_eq!(v[4], 20u); + assert_eq!(v[5], 20u); + } + + #[test] + fn test_is_empty() { + assert!(is_empty::<int>(~[])); + assert!(!is_empty(~[0])); + } + + #[test] + fn test_len_divzero() { + type Z = [i8, ..0]; + let v0 : &[Z] = &[]; + let v1 : &[Z] = &[[]]; + let v2 : &[Z] = &[[], []]; + assert_eq!(sys::size_of::<Z>(), 0); + assert_eq!(v0.len(), 0); + assert_eq!(v1.len(), 1); + assert_eq!(v2.len(), 2); + } + + #[test] + fn test_head() { + let mut a = ~[11]; + assert_eq!(a.head(), &11); + a = ~[11, 12]; + assert_eq!(a.head(), &11); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_head_empty() { + let a: ~[int] = ~[]; + a.head(); + } + + #[test] + fn test_head_opt() { + let mut a = ~[]; + assert_eq!(a.head_opt(), None); + a = ~[11]; + assert_eq!(a.head_opt().unwrap(), &11); + a = ~[11, 12]; + assert_eq!(a.head_opt().unwrap(), &11); + } + + #[test] + fn test_tail() { + let mut a = ~[11]; + assert_eq!(a.tail(), &[]); + a = ~[11, 12]; + assert_eq!(a.tail(), &[12]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_tail_empty() { + let a: ~[int] = ~[]; + a.tail(); + } + + #[test] + fn test_tailn() { + let mut a = ~[11, 12, 13]; + assert_eq!(a.tailn(0), &[11, 12, 13]); + a = ~[11, 12, 13]; + assert_eq!(a.tailn(2), &[13]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_tailn_empty() { + let a: ~[int] = ~[]; + a.tailn(2); + } + + #[test] + fn test_init() { + let mut a = ~[11]; + assert_eq!(a.init(), &[]); + a = ~[11, 12]; + assert_eq!(a.init(), &[11]); + } + + #[init] + #[should_fail] + #[ignore(cfg(windows))] + fn test_init_empty() { + let a: ~[int] = ~[]; + a.init(); + } + + #[test] + fn test_initn() { + let mut a = ~[11, 12, 13]; + assert_eq!(a.initn(0), &[11, 12, 13]); + a = ~[11, 12, 13]; + assert_eq!(a.initn(2), &[11]); + } + + #[init] + #[should_fail] + #[ignore(cfg(windows))] + fn test_initn_empty() { + let a: ~[int] = ~[]; + a.initn(2); + } + + #[test] + fn test_last() { + let mut a = ~[11]; + assert_eq!(a.last(), &11); + a = ~[11, 12]; + assert_eq!(a.last(), &12); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_last_empty() { + let a: ~[int] = ~[]; + a.last(); + } + + #[test] + fn test_last_opt() { + let mut a = ~[]; + assert_eq!(a.last_opt(), None); + a = ~[11]; + assert_eq!(a.last_opt().unwrap(), &11); + a = ~[11, 12]; + assert_eq!(a.last_opt().unwrap(), &12); + } + + #[test] + fn test_slice() { + // Test fixed length vector. + let vec_fixed = [1, 2, 3, 4]; + let v_a = slice(vec_fixed, 1u, vec_fixed.len()).to_vec(); + assert_eq!(v_a.len(), 3u); + assert_eq!(v_a[0], 2); + assert_eq!(v_a[1], 3); + assert_eq!(v_a[2], 4); + + // Test on stack. + let vec_stack = &[1, 2, 3]; + let v_b = slice(vec_stack, 1u, 3u).to_vec(); + assert_eq!(v_b.len(), 2u); + assert_eq!(v_b[0], 2); + assert_eq!(v_b[1], 3); + + // Test on managed heap. + let vec_managed = @[1, 2, 3, 4, 5]; + let v_c = slice(vec_managed, 0u, 3u).to_vec(); + assert_eq!(v_c.len(), 3u); + assert_eq!(v_c[0], 1); + assert_eq!(v_c[1], 2); + assert_eq!(v_c[2], 3); + + // Test on exchange heap. + let vec_unique = ~[1, 2, 3, 4, 5, 6]; + let v_d = slice(vec_unique, 1u, 6u).to_vec(); + assert_eq!(v_d.len(), 5u); + assert_eq!(v_d[0], 2); + assert_eq!(v_d[1], 3); + assert_eq!(v_d[2], 4); + assert_eq!(v_d[3], 5); + assert_eq!(v_d[4], 6); + } + + #[test] + fn test_pop() { + // Test on-heap pop. + let mut v = ~[1, 2, 3, 4, 5]; + let e = v.pop(); + assert_eq!(v.len(), 4u); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + assert_eq!(v[3], 4); + assert_eq!(e, 5); + } + + #[test] + fn test_swap_remove() { + let mut v = ~[1, 2, 3, 4, 5]; + let mut e = v.swap_remove(0); + assert_eq!(v.len(), 4); + assert_eq!(e, 1); + assert_eq!(v[0], 5); + e = v.swap_remove(3); + assert_eq!(v.len(), 3); + assert_eq!(e, 4); + assert_eq!(v[0], 5); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + } + + #[test] + fn test_swap_remove_noncopyable() { + // Tests that we don't accidentally run destructors twice. + let mut v = ~[::unstable::sync::exclusive(()), + ::unstable::sync::exclusive(()), + ::unstable::sync::exclusive(())]; + let mut _e = v.swap_remove(0); + assert_eq!(v.len(), 2); + _e = v.swap_remove(1); + assert_eq!(v.len(), 1); + _e = v.swap_remove(0); + assert_eq!(v.len(), 0); + } + + #[test] + fn test_push() { + // Test on-stack push(). + let mut v = ~[]; + v.push(1); + assert_eq!(v.len(), 1u); + assert_eq!(v[0], 1); + + // Test on-heap push(). + v.push(2); + assert_eq!(v.len(), 2u); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + } + + #[test] + fn test_grow() { + // Test on-stack grow(). + let mut v = ~[]; + v.grow(2u, &1); + assert_eq!(v.len(), 2u); + assert_eq!(v[0], 1); + assert_eq!(v[1], 1); + + // Test on-heap grow(). + v.grow(3u, &2); + assert_eq!(v.len(), 5u); + assert_eq!(v[0], 1); + assert_eq!(v[1], 1); + assert_eq!(v[2], 2); + assert_eq!(v[3], 2); + assert_eq!(v[4], 2); + } + + #[test] + fn test_grow_fn() { + let mut v = ~[]; + v.grow_fn(3u, square); + assert_eq!(v.len(), 3u); + assert_eq!(v[0], 0u); + assert_eq!(v[1], 1u); + assert_eq!(v[2], 4u); + } + + #[test] + fn test_grow_set() { + let mut v = ~[1, 2, 3]; + v.grow_set(4u, &4, 5); + assert_eq!(v.len(), 5u); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + assert_eq!(v[3], 4); + assert_eq!(v[4], 5); + } + + #[test] + fn test_truncate() { + let mut v = ~[@6,@5,@4]; + v.truncate(1); + assert_eq!(v.len(), 1); + assert_eq!(*(v[0]), 6); + // If the unsafe block didn't drop things properly, we blow up here. + } + + #[test] + fn test_clear() { + let mut v = ~[@6,@5,@4]; + v.clear(); + assert_eq!(v.len(), 0); + // If the unsafe block didn't drop things properly, we blow up here. + } + + #[test] + fn test_dedup() { + fn case(a: ~[uint], b: ~[uint]) { + let mut v = a; + v.dedup(); + assert_eq!(v, b); + } + case(~[], ~[]); + case(~[1], ~[1]); + case(~[1,1], ~[1]); + case(~[1,2,3], ~[1,2,3]); + case(~[1,1,2,3], ~[1,2,3]); + case(~[1,2,2,3], ~[1,2,3]); + case(~[1,2,3,3], ~[1,2,3]); + case(~[1,1,2,2,2,3,3], ~[1,2,3]); + } + + #[test] + fn test_dedup_unique() { + let mut v0 = ~[~1, ~1, ~2, ~3]; + v0.dedup(); + let mut v1 = ~[~1, ~2, ~2, ~3]; + v1.dedup(); + let mut v2 = ~[~1, ~2, ~3, ~3]; + v2.dedup(); + /* + * If the ~pointers were leaked or otherwise misused, valgrind and/or + * rustrt should raise errors. + */ + } + + #[test] + fn test_dedup_shared() { + let mut v0 = ~[@1, @1, @2, @3]; + v0.dedup(); + let mut v1 = ~[@1, @2, @2, @3]; + v1.dedup(); + let mut v2 = ~[@1, @2, @3, @3]; + v2.dedup(); + /* + * If the @pointers were leaked or otherwise misused, valgrind and/or + * rustrt should raise errors. + */ + } + + #[test] + fn test_map() { + // Test on-stack map. + let mut v = ~[1u, 2u, 3u]; + let mut w = map(v, square_ref); + assert_eq!(w.len(), 3u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 4u); + assert_eq!(w[2], 9u); + + // Test on-heap map. + v = ~[1u, 2u, 3u, 4u, 5u]; + w = map(v, square_ref); + assert_eq!(w.len(), 5u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 4u); + assert_eq!(w[2], 9u); + assert_eq!(w[3], 16u); + assert_eq!(w[4], 25u); + } + + #[test] + fn test_map_zip() { + fn times(x: &int, y: &int) -> int { *x * *y } + let f = times; + let v0 = ~[1, 2, 3, 4, 5]; + let v1 = ~[5, 4, 3, 2, 1]; + let u = map_zip::<int, int, int>(v0, v1, f); + let mut i = 0; + while i < 5 { assert!(v0[i] * v1[i] == u[i]); i += 1; } + } + + #[test] + fn test_filter_mapped() { + // Test on-stack filter-map. + let mut v = ~[1u, 2u, 3u]; + let mut w = filter_mapped(v, square_if_odd_r); + assert_eq!(w.len(), 2u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 9u); + + // Test on-heap filter-map. + v = ~[1u, 2u, 3u, 4u, 5u]; + w = filter_mapped(v, square_if_odd_r); + assert_eq!(w.len(), 3u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 9u); + assert_eq!(w[2], 25u); + + fn halve(i: &int) -> Option<int> { + if *i % 2 == 0 { + Some::<int>(*i / 2) + } else { + None::<int> + } + } + fn halve_for_sure(i: &int) -> int { *i / 2 } + let all_even: ~[int] = ~[0, 2, 8, 6]; + let all_odd1: ~[int] = ~[1, 7, 3]; + let all_odd2: ~[int] = ~[]; + let mix: ~[int] = ~[9, 2, 6, 7, 1, 0, 0, 3]; + let mix_dest: ~[int] = ~[1, 3, 0, 0]; + assert!(filter_mapped(all_even, halve) == + map(all_even, halve_for_sure)); + assert_eq!(filter_mapped(all_odd1, halve), ~[]); + assert_eq!(filter_mapped(all_odd2, halve), ~[]); + assert_eq!(filter_mapped(mix, halve), mix_dest); + } + + #[test] + fn test_filter_map() { + // Test on-stack filter-map. + let mut v = ~[1u, 2u, 3u]; + let mut w = filter_map(v, square_if_odd_v); + assert_eq!(w.len(), 2u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 9u); + + // Test on-heap filter-map. + v = ~[1u, 2u, 3u, 4u, 5u]; + w = filter_map(v, square_if_odd_v); + assert_eq!(w.len(), 3u); + assert_eq!(w[0], 1u); + assert_eq!(w[1], 9u); + assert_eq!(w[2], 25u); + + fn halve(i: int) -> Option<int> { + if i % 2 == 0 { + Some::<int>(i / 2) + } else { + None::<int> + } + } + fn halve_for_sure(i: &int) -> int { *i / 2 } + let all_even: ~[int] = ~[0, 2, 8, 6]; + let all_even0: ~[int] = copy all_even; + let all_odd1: ~[int] = ~[1, 7, 3]; + let all_odd2: ~[int] = ~[]; + let mix: ~[int] = ~[9, 2, 6, 7, 1, 0, 0, 3]; + let mix_dest: ~[int] = ~[1, 3, 0, 0]; + assert!(filter_map(all_even, halve) == + map(all_even0, halve_for_sure)); + assert_eq!(filter_map(all_odd1, halve), ~[]); + assert_eq!(filter_map(all_odd2, halve), ~[]); + assert_eq!(filter_map(mix, halve), mix_dest); + } + + #[test] + fn test_filter() { + assert_eq!(filter(~[1u, 2u, 3u], is_odd), ~[1u, 3u]); + assert_eq!(filter(~[1u, 2u, 4u, 8u, 16u], is_three), ~[]); + } + + #[test] + fn test_retain() { + let mut v = ~[1, 2, 3, 4, 5]; + v.retain(is_odd); + assert_eq!(v, ~[1, 3, 5]); + } + + #[test] + fn test_foldl() { + // Test on-stack fold. + let mut v = ~[1u, 2u, 3u]; + let mut sum = foldl(0u, v, add); + assert_eq!(sum, 6u); + + // Test on-heap fold. + v = ~[1u, 2u, 3u, 4u, 5u]; + sum = foldl(0u, v, add); + assert_eq!(sum, 15u); + } + + #[test] + fn test_foldl2() { + fn sub(a: int, b: &int) -> int { + a - *b + } + let v = ~[1, 2, 3, 4]; + let sum = foldl(0, v, sub); + assert_eq!(sum, -10); + } + + #[test] + fn test_foldr() { + fn sub(a: &int, b: int) -> int { + *a - b + } + let v = ~[1, 2, 3, 4]; + let sum = foldr(v, 0, sub); + assert_eq!(sum, -2); + } + + #[test] + fn test_each_empty() { + for each::<int>(~[]) |_v| { + fail!(); // should never be executed + } + } + + #[test] + fn test_each_nonempty() { + let mut i = 0; + for each(~[1, 2, 3]) |v| { + i += *v; + } + assert_eq!(i, 6); + } + + #[test] + fn test_eachi() { + let mut i = 0; + for eachi(~[1, 2, 3]) |j, v| { + if i == 0 { assert!(*v == 1); } + assert_eq!(j + 1u, *v as uint); + i += *v; + } + assert_eq!(i, 6); + } + + #[test] + fn test_each_reverse_empty() { + let v: ~[int] = ~[]; + for v.each_reverse |_v| { + fail!(); // should never execute + } + } + + #[test] + fn test_each_reverse_nonempty() { + let mut i = 0; + for each_reverse(~[1, 2, 3]) |v| { + if i == 0 { assert!(*v == 3); } + i += *v + } + assert_eq!(i, 6); + } + + #[test] + fn test_eachi_reverse() { + let mut i = 0; + for eachi_reverse(~[0, 1, 2]) |j, v| { + if i == 0 { assert!(*v == 2); } + assert_eq!(j, *v as uint); + i += *v; + } + assert_eq!(i, 3); + } + + #[test] + fn test_eachi_reverse_empty() { + let v: ~[int] = ~[]; + for v.eachi_reverse |_i, _v| { + fail!(); // should never execute + } + } + + #[test] + fn test_each_permutation() { + let mut results: ~[~[int]]; + + results = ~[]; + for each_permutation(~[]) |v| { results.push(to_owned(v)); } + assert_eq!(results, ~[~[]]); + + results = ~[]; + for each_permutation(~[7]) |v| { results.push(to_owned(v)); } + assert_eq!(results, ~[~[7]]); + + results = ~[]; + for each_permutation(~[1,1]) |v| { results.push(to_owned(v)); } + assert_eq!(results, ~[~[1,1],~[1,1]]); + + results = ~[]; + for each_permutation(~[5,2,0]) |v| { results.push(to_owned(v)); } + assert!(results == + ~[~[5,2,0],~[5,0,2],~[2,5,0],~[2,0,5],~[0,5,2],~[0,2,5]]); + } + + #[test] + fn test_any_and_all() { + assert!(any(~[1u, 2u, 3u], is_three)); + assert!(!any(~[0u, 1u, 2u], is_three)); + assert!(any(~[1u, 2u, 3u, 4u, 5u], is_three)); + assert!(!any(~[1u, 2u, 4u, 5u, 6u], is_three)); + + assert!(all(~[3u, 3u, 3u], is_three)); + assert!(!all(~[3u, 3u, 2u], is_three)); + assert!(all(~[3u, 3u, 3u, 3u, 3u], is_three)); + assert!(!all(~[3u, 3u, 0u, 1u, 2u], is_three)); + } + + #[test] + fn test_any2_and_all2() { + + assert!(any2(~[2u, 4u, 6u], ~[2u, 4u, 6u], is_equal)); + assert!(any2(~[1u, 2u, 3u], ~[4u, 5u, 3u], is_equal)); + assert!(!any2(~[1u, 2u, 3u], ~[4u, 5u, 6u], is_equal)); + assert!(any2(~[2u, 4u, 6u], ~[2u, 4u], is_equal)); + + assert!(all2(~[2u, 4u, 6u], ~[2u, 4u, 6u], is_equal)); + assert!(!all2(~[1u, 2u, 3u], ~[4u, 5u, 3u], is_equal)); + assert!(!all2(~[1u, 2u, 3u], ~[4u, 5u, 6u], is_equal)); + assert!(!all2(~[2u, 4u, 6u], ~[2u, 4u], is_equal)); + } + + #[test] + fn test_zip_unzip() { + let v1 = ~[1, 2, 3]; + let v2 = ~[4, 5, 6]; + + let z1 = zip(v1, v2); + + assert_eq!((1, 4), z1[0]); + assert_eq!((2, 5), z1[1]); + assert_eq!((3, 6), z1[2]); + + let (left, right) = unzip(z1); + + assert_eq!((1, 4), (left[0], right[0])); + assert_eq!((2, 5), (left[1], right[1])); + assert_eq!((3, 6), (left[2], right[2])); + } + + #[test] + fn test_position_elem() { + assert!(position_elem(~[], &1).is_none()); + + let v1 = ~[1, 2, 3, 3, 2, 5]; + assert_eq!(position_elem(v1, &1), Some(0u)); + assert_eq!(position_elem(v1, &2), Some(1u)); + assert_eq!(position_elem(v1, &5), Some(5u)); + assert!(position_elem(v1, &4).is_none()); + } + + #[test] + fn test_position() { + fn less_than_three(i: &int) -> bool { *i < 3 } + fn is_eighteen(i: &int) -> bool { *i == 18 } + + assert!(position(~[], less_than_three).is_none()); + + let v1 = ~[5, 4, 3, 2, 1]; + assert_eq!(position(v1, less_than_three), Some(3u)); + assert!(position(v1, is_eighteen).is_none()); + } + + #[test] + fn test_position_between() { + assert!(position_between(~[], 0u, 0u, f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert!(position_between(v, 0u, 0u, f).is_none()); + assert!(position_between(v, 0u, 1u, f).is_none()); + assert_eq!(position_between(v, 0u, 2u, f), Some(1u)); + assert_eq!(position_between(v, 0u, 3u, f), Some(1u)); + assert_eq!(position_between(v, 0u, 4u, f), Some(1u)); + + assert!(position_between(v, 1u, 1u, f).is_none()); + assert_eq!(position_between(v, 1u, 2u, f), Some(1u)); + assert_eq!(position_between(v, 1u, 3u, f), Some(1u)); + assert_eq!(position_between(v, 1u, 4u, f), Some(1u)); + + assert!(position_between(v, 2u, 2u, f).is_none()); + assert!(position_between(v, 2u, 3u, f).is_none()); + assert_eq!(position_between(v, 2u, 4u, f), Some(3u)); + + assert!(position_between(v, 3u, 3u, f).is_none()); + assert_eq!(position_between(v, 3u, 4u, f), Some(3u)); + + assert!(position_between(v, 4u, 4u, f).is_none()); + } + + #[test] + fn test_find() { + assert!(find(~[], f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + fn g(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'd' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert_eq!(find(v, f), Some((1, 'b'))); + assert!(find(v, g).is_none()); + } + + #[test] + fn test_find_between() { + assert!(find_between(~[], 0u, 0u, f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert!(find_between(v, 0u, 0u, f).is_none()); + assert!(find_between(v, 0u, 1u, f).is_none()); + assert_eq!(find_between(v, 0u, 2u, f), Some((1, 'b'))); + assert_eq!(find_between(v, 0u, 3u, f), Some((1, 'b'))); + assert_eq!(find_between(v, 0u, 4u, f), Some((1, 'b'))); + + assert!(find_between(v, 1u, 1u, f).is_none()); + assert_eq!(find_between(v, 1u, 2u, f), Some((1, 'b'))); + assert_eq!(find_between(v, 1u, 3u, f), Some((1, 'b'))); + assert_eq!(find_between(v, 1u, 4u, f), Some((1, 'b'))); + + assert!(find_between(v, 2u, 2u, f).is_none()); + assert!(find_between(v, 2u, 3u, f).is_none()); + assert_eq!(find_between(v, 2u, 4u, f), Some((3, 'b'))); + + assert!(find_between(v, 3u, 3u, f).is_none()); + assert_eq!(find_between(v, 3u, 4u, f), Some((3, 'b'))); + + assert!(find_between(v, 4u, 4u, f).is_none()); + } + + #[test] + fn test_rposition() { + assert!(find(~[], f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + fn g(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'd' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert_eq!(position(v, f), Some(1u)); + assert!(position(v, g).is_none()); + } + + #[test] + fn test_rposition_between() { + assert!(rposition_between(~[], 0u, 0u, f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert!(rposition_between(v, 0u, 0u, f).is_none()); + assert!(rposition_between(v, 0u, 1u, f).is_none()); + assert_eq!(rposition_between(v, 0u, 2u, f), Some(1u)); + assert_eq!(rposition_between(v, 0u, 3u, f), Some(1u)); + assert_eq!(rposition_between(v, 0u, 4u, f), Some(3u)); + + assert!(rposition_between(v, 1u, 1u, f).is_none()); + assert_eq!(rposition_between(v, 1u, 2u, f), Some(1u)); + assert_eq!(rposition_between(v, 1u, 3u, f), Some(1u)); + assert_eq!(rposition_between(v, 1u, 4u, f), Some(3u)); + + assert!(rposition_between(v, 2u, 2u, f).is_none()); + assert!(rposition_between(v, 2u, 3u, f).is_none()); + assert_eq!(rposition_between(v, 2u, 4u, f), Some(3u)); + + assert!(rposition_between(v, 3u, 3u, f).is_none()); + assert_eq!(rposition_between(v, 3u, 4u, f), Some(3u)); + + assert!(rposition_between(v, 4u, 4u, f).is_none()); + } + + #[test] + fn test_rfind() { + assert!(rfind(~[], f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + fn g(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'd' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert_eq!(rfind(v, f), Some((3, 'b'))); + assert!(rfind(v, g).is_none()); + } + + #[test] + fn test_rfind_between() { + assert!(rfind_between(~[], 0u, 0u, f).is_none()); + + fn f(xy: &(int, char)) -> bool { let (_x, y) = *xy; y == 'b' } + let v = ~[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert!(rfind_between(v, 0u, 0u, f).is_none()); + assert!(rfind_between(v, 0u, 1u, f).is_none()); + assert_eq!(rfind_between(v, 0u, 2u, f), Some((1, 'b'))); + assert_eq!(rfind_between(v, 0u, 3u, f), Some((1, 'b'))); + assert_eq!(rfind_between(v, 0u, 4u, f), Some((3, 'b'))); + + assert!(rfind_between(v, 1u, 1u, f).is_none()); + assert_eq!(rfind_between(v, 1u, 2u, f), Some((1, 'b'))); + assert_eq!(rfind_between(v, 1u, 3u, f), Some((1, 'b'))); + assert_eq!(rfind_between(v, 1u, 4u, f), Some((3, 'b'))); + + assert!(rfind_between(v, 2u, 2u, f).is_none()); + assert!(rfind_between(v, 2u, 3u, f).is_none()); + assert_eq!(rfind_between(v, 2u, 4u, f), Some((3, 'b'))); + + assert!(rfind_between(v, 3u, 3u, f).is_none()); + assert_eq!(rfind_between(v, 3u, 4u, f), Some((3, 'b'))); + + assert!(rfind_between(v, 4u, 4u, f).is_none()); + } + + #[test] + fn test_bsearch_elem() { + assert_eq!(bsearch_elem([1,2,3,4,5], &5), Some(4)); + assert_eq!(bsearch_elem([1,2,3,4,5], &4), Some(3)); + assert_eq!(bsearch_elem([1,2,3,4,5], &3), Some(2)); + assert_eq!(bsearch_elem([1,2,3,4,5], &2), Some(1)); + assert_eq!(bsearch_elem([1,2,3,4,5], &1), Some(0)); + + assert_eq!(bsearch_elem([2,4,6,8,10], &1), None); + assert_eq!(bsearch_elem([2,4,6,8,10], &5), None); + assert_eq!(bsearch_elem([2,4,6,8,10], &4), Some(1)); + assert_eq!(bsearch_elem([2,4,6,8,10], &10), Some(4)); + + assert_eq!(bsearch_elem([2,4,6,8], &1), None); + assert_eq!(bsearch_elem([2,4,6,8], &5), None); + assert_eq!(bsearch_elem([2,4,6,8], &4), Some(1)); + assert_eq!(bsearch_elem([2,4,6,8], &8), Some(3)); + + assert_eq!(bsearch_elem([2,4,6], &1), None); + assert_eq!(bsearch_elem([2,4,6], &5), None); + assert_eq!(bsearch_elem([2,4,6], &4), Some(1)); + assert_eq!(bsearch_elem([2,4,6], &6), Some(2)); + + assert_eq!(bsearch_elem([2,4], &1), None); + assert_eq!(bsearch_elem([2,4], &5), None); + assert_eq!(bsearch_elem([2,4], &2), Some(0)); + assert_eq!(bsearch_elem([2,4], &4), Some(1)); + + assert_eq!(bsearch_elem([2], &1), None); + assert_eq!(bsearch_elem([2], &5), None); + assert_eq!(bsearch_elem([2], &2), Some(0)); + + assert_eq!(bsearch_elem([], &1), None); + assert_eq!(bsearch_elem([], &5), None); + + assert!(bsearch_elem([1,1,1,1,1], &1) != None); + assert!(bsearch_elem([1,1,1,1,2], &1) != None); + assert!(bsearch_elem([1,1,1,2,2], &1) != None); + assert!(bsearch_elem([1,1,2,2,2], &1) != None); + assert_eq!(bsearch_elem([1,2,2,2,2], &1), Some(0)); + + assert_eq!(bsearch_elem([1,2,3,4,5], &6), None); + assert_eq!(bsearch_elem([1,2,3,4,5], &0), None); + } + + #[test] + fn reverse_and_reversed() { + let mut v: ~[int] = ~[10, 20]; + assert_eq!(v[0], 10); + assert_eq!(v[1], 20); + reverse(v); + assert_eq!(v[0], 20); + assert_eq!(v[1], 10); + let v2 = reversed::<int>(~[10, 20]); + assert_eq!(v2[0], 20); + assert_eq!(v2[1], 10); + v[0] = 30; + assert_eq!(v2[0], 20); + // Make sure they work with 0-length vectors too. + + let v4 = reversed::<int>(~[]); + assert_eq!(v4, ~[]); + let mut v3: ~[int] = ~[]; + reverse::<int>(v3); + } + + #[test] + fn reversed_mut() { + let v2 = reversed::<int>(~[10, 20]); + assert_eq!(v2[0], 20); + assert_eq!(v2[1], 10); + } + + #[test] + fn test_split() { + fn f(x: &int) -> bool { *x == 3 } + + assert_eq!(split(~[], f), ~[]); + assert_eq!(split(~[1, 2], f), ~[~[1, 2]]); + assert_eq!(split(~[3, 1, 2], f), ~[~[], ~[1, 2]]); + assert_eq!(split(~[1, 2, 3], f), ~[~[1, 2], ~[]]); + assert_eq!(split(~[1, 2, 3, 4, 3, 5], f), ~[~[1, 2], ~[4], ~[5]]); + } + + #[test] + fn test_splitn() { + fn f(x: &int) -> bool { *x == 3 } + + assert_eq!(splitn(~[], 1u, f), ~[]); + assert_eq!(splitn(~[1, 2], 1u, f), ~[~[1, 2]]); + assert_eq!(splitn(~[3, 1, 2], 1u, f), ~[~[], ~[1, 2]]); + assert_eq!(splitn(~[1, 2, 3], 1u, f), ~[~[1, 2], ~[]]); + assert!(splitn(~[1, 2, 3, 4, 3, 5], 1u, f) == + ~[~[1, 2], ~[4, 3, 5]]); + } + + #[test] + fn test_rsplit() { + fn f(x: &int) -> bool { *x == 3 } + + assert_eq!(rsplit(~[], f), ~[]); + assert_eq!(rsplit(~[1, 2], f), ~[~[1, 2]]); + assert_eq!(rsplit(~[1, 2, 3], f), ~[~[1, 2], ~[]]); + assert!(rsplit(~[1, 2, 3, 4, 3, 5], f) == + ~[~[1, 2], ~[4], ~[5]]); + } + + #[test] + fn test_rsplitn() { + fn f(x: &int) -> bool { *x == 3 } + + assert_eq!(rsplitn(~[], 1u, f), ~[]); + assert_eq!(rsplitn(~[1, 2], 1u, f), ~[~[1, 2]]); + assert_eq!(rsplitn(~[1, 2, 3], 1u, f), ~[~[1, 2], ~[]]); + assert!(rsplitn(~[1, 2, 3, 4, 3, 5], 1u, f) == + ~[~[1, 2, 3, 4], ~[5]]); + } + + #[test] + fn test_partition() { + // FIXME (#4355 maybe): using v.partition here crashes + assert_eq!(partition(~[], |x: &int| *x < 3), (~[], ~[])); + assert!(partition(~[1, 2, 3], |x: &int| *x < 4) == + (~[1, 2, 3], ~[])); + assert!(partition(~[1, 2, 3], |x: &int| *x < 2) == + (~[1], ~[2, 3])); + assert!(partition(~[1, 2, 3], |x: &int| *x < 0) == + (~[], ~[1, 2, 3])); + } + + #[test] + fn test_partitioned() { + assert_eq!((~[]).partitioned(|x: &int| *x < 3), (~[], ~[])) + assert!((~[1, 2, 3]).partitioned(|x: &int| *x < 4) == + (~[1, 2, 3], ~[])); + assert!((~[1, 2, 3]).partitioned(|x: &int| *x < 2) == + (~[1], ~[2, 3])); + assert!((~[1, 2, 3]).partitioned(|x: &int| *x < 0) == + (~[], ~[1, 2, 3])); + } + + #[test] + fn test_concat() { + assert_eq!(concat(~[~[1], ~[2,3]]), ~[1, 2, 3]); + } + + #[test] + fn test_connect() { + assert_eq!(connect(~[], &0), ~[]); + assert_eq!(connect(~[~[1], ~[2, 3]], &0), ~[1, 0, 2, 3]); + assert_eq!(connect(~[~[1], ~[2], ~[3]], &0), ~[1, 0, 2, 0, 3]); + } + + #[test] + fn test_windowed () { + fn t(n: uint, expected: &[&[int]]) { + let mut i = 0; + for windowed(n, ~[1,2,3,4,5,6]) |v| { + assert_eq!(v, expected[i]); + i += 1; + } + + // check that we actually iterated the right number of times + assert_eq!(i, expected.len()); + } + t(3, &[&[1,2,3],&[2,3,4],&[3,4,5],&[4,5,6]]); + t(4, &[&[1,2,3,4],&[2,3,4,5],&[3,4,5,6]]); + t(7, &[]); + t(8, &[]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_windowed_() { + for windowed (0u, ~[1u,2u,3u,4u,5u,6u]) |_v| {} + } + + #[test] + fn test_unshift() { + let mut x = ~[1, 2, 3]; + x.unshift(0); + assert_eq!(x, ~[0, 1, 2, 3]); + } + + #[test] + fn test_insert() { + let mut a = ~[1, 2, 4]; + a.insert(2, 3); + assert_eq!(a, ~[1, 2, 3, 4]); + + let mut a = ~[1, 2, 3]; + a.insert(0, 0); + assert_eq!(a, ~[0, 1, 2, 3]); + + let mut a = ~[1, 2, 3]; + a.insert(3, 4); + assert_eq!(a, ~[1, 2, 3, 4]); + + let mut a = ~[]; + a.insert(0, 1); + assert_eq!(a, ~[1]); + } + + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_insert_oob() { + let mut a = ~[1, 2, 3]; + a.insert(4, 5); + } + + #[test] + fn test_remove() { + let mut a = ~[1, 2, 3, 4]; + a.remove(2); + assert_eq!(a, ~[1, 2, 4]); + + let mut a = ~[1, 2, 3]; + a.remove(0); + assert_eq!(a, ~[2, 3]); + + let mut a = ~[1]; + a.remove(0); + assert_eq!(a, ~[]); + } + + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_remove_oob() { + let mut a = ~[1, 2, 3]; + a.remove(3); + } + + #[test] + fn test_capacity() { + let mut v = ~[0u64]; + reserve(&mut v, 10u); + assert_eq!(capacity(&v), 10u); + let mut v = ~[0u32]; + reserve(&mut v, 10u); + assert_eq!(capacity(&v), 10u); + } + + #[test] + fn test_slice_2() { + let v = ~[1, 2, 3, 4, 5]; + let v = v.slice(1u, 3u); + assert_eq!(v.len(), 2u); + assert_eq!(v[0], 2); + assert_eq!(v[1], 3); + } + + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_from_fn_fail() { + do from_fn(100) |v| { + if v == 50 { fail!() } + (~0, @0) + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_build_fail() { + do build |push| { + push((~0, @0)); + push((~0, @0)); + push((~0, @0)); + push((~0, @0)); + fail!(); + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_split_fail_ret_true() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do split(v) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_split_fail_ret_false() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do split(v) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_splitn_fail_ret_true() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do splitn(v, 100) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_splitn_fail_ret_false() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do split(v) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_rsplit_fail_ret_true() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do rsplit(v) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_rsplit_fail_ret_false() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do rsplit(v) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_rsplitn_fail_ret_true() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do rsplitn(v, 100) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_rsplitn_fail_ret_false() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do rsplitn(v, 100) |_elt| { + if i == 2 { + fail!() + } + i += 1; + + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_consume_fail() { + let v = ~[(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do consume(v) |_i, _elt| { + if i == 2 { + fail!() + } + i += 1; + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_grow_fn_fail() { + let mut v = ~[]; + do v.grow_fn(100) |i| { + if i == 50 { + fail!() + } + (~0, @0) + } + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_map_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do map(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + ~[(~0, @0)] + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_map_consume_fail() { + let v = ~[(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do map_consume(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + ~[(~0, @0)] + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_mapi_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do mapi(v) |_i, _elt| { + if i == 2 { + fail!() + } + i += 0; + ~[(~0, @0)] + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_flat_map_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do map(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + ~[(~0, @0)] + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_map_zip_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do map_zip(v, v) |_elt1, _elt2| { + if i == 2 { + fail!() + } + i += 0; + ~[(~0, @0)] + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_filter_mapped_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do filter_mapped(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + Some((~0, @0)) + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_filter_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do v.filtered |_elt| { + if i == 2 { + fail!() + } + i += 0; + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_foldl_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do foldl((~0, @0), v) |_a, _b| { + if i == 2 { + fail!() + } + i += 0; + (~0, @0) + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_foldr_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do foldr(v, (~0, @0)) |_a, _b| { + if i == 2 { + fail!() + } + i += 0; + (~0, @0) + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_any_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do any(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_any2_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do any(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_all_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do all(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_alli_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do alli(v) |_i, _elt| { + if i == 2 { + fail!() + } + i += 0; + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_all2_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do all2(v, v) |_elt1, _elt2| { + if i == 2 { + fail!() + } + i += 0; + true + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_find_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do find(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_position_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do position(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_rposition_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do rposition(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_each_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do each(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_eachi_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + do eachi(v) |_i, _elt| { + if i == 2 { + fail!() + } + i += 0; + false + }; + } + + #[test] + #[ignore(windows)] + #[should_fail] + #[allow(non_implicitly_copyable_typarams)] + fn test_permute_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + let mut i = 0; + for each_permutation(v) |_elt| { + if i == 2 { + fail!() + } + i += 0; + } + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_as_imm_buf_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + do as_imm_buf(v) |_buf, _i| { + fail!() + } + } + + #[test] + #[ignore(windows)] + #[should_fail] + fn test_as_const_buf_fail() { + let v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + do as_const_buf(v) |_buf, _i| { + fail!() + } + } + + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_as_mut_buf_fail() { + let mut v = [(~0, @0), (~0, @0), (~0, @0), (~0, @0)]; + do as_mut_buf(v) |_buf, _i| { + fail!() + } + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_copy_memory_oob() { + unsafe { + let mut a = [1, 2, 3, 4]; + let b = [1, 2, 3, 4, 5]; + raw::copy_memory(a, b, 5); + } + } + + #[test] + fn test_total_ord() { + [1, 2, 3, 4].cmp(& &[1, 2, 3]) == Greater; + [1, 2, 3].cmp(& &[1, 2, 3, 4]) == Less; + [1, 2, 3, 4].cmp(& &[1, 2, 3, 4]) == Equal; + [1, 2, 3, 4, 5, 5, 5, 5].cmp(& &[1, 2, 3, 4, 5, 6]) == Less; + [2, 2].cmp(& &[1, 2, 3, 4]) == Greater; + } + + #[test] + fn test_iterator() { + use iterator::*; + let xs = [1, 2, 5, 10, 11]; + let ys = [1, 2, 5, 10, 11, 19]; + let mut it = xs.iter(); + let mut i = 0; + for it.advance |&x| { + assert_eq!(x, ys[i]); + i += 1; + } + } + + #[test] + fn test_reverse_part() { + let mut values = [1,2,3,4,5]; + reverse_part(values,1,4); + assert_eq!(values, [1,4,3,2,5]); + } + + #[test] + fn test_permutations0() { + let values = []; + let mut v : ~[~[int]] = ~[]; + for each_permutation(values) |p| { + v.push(p.to_owned()); + } + assert_eq!(v, ~[~[]]); + } + + #[test] + fn test_permutations1() { + let values = [1]; + let mut v : ~[~[int]] = ~[]; + for each_permutation(values) |p| { + v.push(p.to_owned()); + } + assert_eq!(v, ~[~[1]]); + } + + #[test] + fn test_permutations2() { + let values = [1,2]; + let mut v : ~[~[int]] = ~[]; + for each_permutation(values) |p| { + v.push(p.to_owned()); + } + assert_eq!(v, ~[~[1,2],~[2,1]]); + } + + #[test] + fn test_permutations3() { + let values = [1,2,3]; + let mut v : ~[~[int]] = ~[]; + for each_permutation(values) |p| { + v.push(p.to_owned()); + } + assert_eq!(v, ~[~[1,2,3],~[1,3,2],~[2,1,3],~[2,3,1],~[3,1,2],~[3,2,1]]); + } + + #[test] + fn test_each_val() { + use old_iter::CopyableNonstrictIter; + let mut i = 0; + for [1, 2, 3].each_val |v| { + i += v; + } + assert_eq!(i, 6); + } +} diff --git a/src/libstd/workcache.rs b/src/libstd/workcache.rs deleted file mode 100644 index ee57bf2f3a1..00000000000 --- a/src/libstd/workcache.rs +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 json; -use sha1; -use serialize::{Encoder, Encodable, Decoder, Decodable}; -use sort; - -use core::cell::Cell; -use core::cmp; -use core::comm::{PortOne, oneshot, send_one, recv_one}; -use core::either::{Either, Left, Right}; -use core::hashmap::HashMap; -use core::io; -use core::run; -use core::to_bytes; -use core::util::replace; - -/** -* -* This is a loose clone of the [fbuild build system](https://github.com/felix-lang/fbuild), -* made a touch more generic (not wired to special cases on files) and much -* less metaprogram-y due to rust's comparative weakness there, relative to -* python. -* -* It's based around _imperative builds_ that happen to have some function -* calls cached. That is, it's _just_ a mechanism for describing cached -* functions. This makes it much simpler and smaller than a "build system" -* that produces an IR and evaluates it. The evaluation order is normal -* function calls. Some of them just return really quickly. -* -* A cached function consumes and produces a set of _works_. A work has a -* name, a kind (that determines how the value is to be checked for -* freshness) and a value. Works must also be (de)serializable. Some -* examples of works: -* -* kind name value -* ------------------------ -* cfg os linux -* file foo.c <sha1> -* url foo.com <etag> -* -* Works are conceptually single units, but we store them most of the time -* in maps of the form (type,name) => value. These are WorkMaps. -* -* A cached function divides the works it's interested in into inputs and -* outputs, and subdivides those into declared (input) works and -* discovered (input and output) works. -* -* A _declared_ input or is one that is given to the workcache before -* any work actually happens, in the "prep" phase. Even when a function's -* work-doing part (the "exec" phase) never gets called, it has declared -* inputs, which can be checked for freshness (and potentially -* used to determine that the function can be skipped). -* -* The workcache checks _all_ works for freshness, but uses the set of -* discovered outputs from the _previous_ exec (which it will re-discover -* and re-record each time the exec phase runs). -* -* Therefore the discovered works cached in the db might be a -* mis-approximation of the current discoverable works, but this is ok for -* the following reason: we assume that if an artifact A changed from -* depending on B,C,D to depending on B,C,D,E, then A itself changed (as -* part of the change-in-dependencies), so we will be ok. -* -* Each function has a single discriminated output work called its _result_. -* This is only different from other works in that it is returned, by value, -* from a call to the cacheable function; the other output works are used in -* passing to invalidate dependencies elsewhere in the cache, but do not -* otherwise escape from a function invocation. Most functions only have one -* output work anyways. -* -* A database (the central store of a workcache) stores a mappings: -* -* (fn_name,{declared_input}) => ({discovered_input}, -* {discovered_output},result) -* -* (Note: fbuild, which workcache is based on, has the concept of a declared -* output as separate from a discovered output. This distinction exists only -* as an artifact of how fbuild works: via annotations on function types -* and metaprogramming, with explicit dependency declaration as a fallback. -* Workcache is more explicit about dependencies, and as such treats all -* outputs the same, as discovered-during-the-last-run.) -* -*/ - -#[deriving(Eq, Encodable, Decodable)] -struct WorkKey { - kind: ~str, - name: ~str -} - -impl to_bytes::IterBytes for WorkKey { - #[inline(always)] - fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool { - self.kind.iter_bytes(lsb0, f) && self.name.iter_bytes(lsb0, f) - } -} - -impl cmp::Ord for WorkKey { - fn lt(&self, other: &WorkKey) -> bool { - self.kind < other.kind || - (self.kind == other.kind && - self.name < other.name) - } - fn le(&self, other: &WorkKey) -> bool { - self.lt(other) || self.eq(other) - } - fn ge(&self, other: &WorkKey) -> bool { - self.gt(other) || self.eq(other) - } - fn gt(&self, other: &WorkKey) -> bool { - ! self.le(other) - } -} - -pub impl WorkKey { - fn new(kind: &str, name: &str) -> WorkKey { - WorkKey { kind: kind.to_owned(), name: name.to_owned() } - } -} - -struct WorkMap(HashMap<WorkKey, ~str>); - -impl WorkMap { - fn new() -> WorkMap { WorkMap(HashMap::new()) } -} - -impl<S:Encoder> Encodable<S> for WorkMap { - fn encode(&self, s: &mut S) { - let mut d = ~[]; - for self.each |k, v| { - d.push((copy *k, copy *v)) - } - sort::tim_sort(d); - d.encode(s) - } -} - -impl<D:Decoder> Decodable<D> for WorkMap { - fn decode(d: &mut D) -> WorkMap { - let v : ~[(WorkKey,~str)] = Decodable::decode(d); - let mut w = WorkMap::new(); - for v.each |&(k, v)| { - w.insert(copy k, copy v); - } - w - } -} - -struct Database { - db_filename: Path, - db_cache: HashMap<~str, ~str>, - db_dirty: bool -} - -pub impl Database { - fn prepare(&mut self, - fn_name: &str, - declared_inputs: &WorkMap) - -> Option<(WorkMap, WorkMap, ~str)> { - let k = json_encode(&(fn_name, declared_inputs)); - match self.db_cache.find(&k) { - None => None, - Some(v) => Some(json_decode(*v)) - } - } - - fn cache(&mut self, - fn_name: &str, - declared_inputs: &WorkMap, - discovered_inputs: &WorkMap, - discovered_outputs: &WorkMap, - result: &str) { - let k = json_encode(&(fn_name, declared_inputs)); - let v = json_encode(&(discovered_inputs, - discovered_outputs, - result)); - self.db_cache.insert(k,v); - self.db_dirty = true - } -} - -struct Logger { - // FIXME #4432: Fill in - a: () -} - -pub impl Logger { - fn info(&self, i: &str) { - io::println(~"workcache: " + i.to_owned()); - } -} - -struct Context { - db: @mut Database, - logger: @mut Logger, - cfg: @json::Object, - freshness: HashMap<~str,@fn(&str,&str)->bool> -} - -struct Prep { - ctxt: @Context, - fn_name: ~str, - declared_inputs: WorkMap, -} - -struct Exec { - discovered_inputs: WorkMap, - discovered_outputs: WorkMap -} - -struct Work<T> { - prep: @mut Prep, - res: Option<Either<T,PortOne<(Exec,T)>>> -} - -fn json_encode<T:Encodable<json::Encoder>>(t: &T) -> ~str { - do io::with_str_writer |wr| { - let mut encoder = json::Encoder(wr); - t.encode(&mut encoder); - } -} - -// FIXME(#5121) -fn json_decode<T:Decodable<json::Decoder>>(s: &str) -> T { - do io::with_str_reader(s) |rdr| { - let j = result::unwrap(json::from_reader(rdr)); - let mut decoder = json::Decoder(j); - Decodable::decode(&mut decoder) - } -} - -fn digest<T:Encodable<json::Encoder>>(t: &T) -> ~str { - let mut sha = sha1::sha1(); - sha.input_str(json_encode(t)); - sha.result_str() -} - -fn digest_file(path: &Path) -> ~str { - let mut sha = sha1::sha1(); - let s = io::read_whole_file_str(path); - sha.input_str(*s.get_ref()); - sha.result_str() -} - -pub impl Context { - - fn new(db: @mut Database, - lg: @mut Logger, - cfg: @json::Object) -> Context { - Context { - db: db, - logger: lg, - cfg: cfg, - freshness: HashMap::new() - } - } - - fn prep<T:Owned + - Encodable<json::Encoder> + - Decodable<json::Decoder>>( // FIXME(#5121) - @self, - fn_name:&str, - blk: &fn(@mut Prep)->Work<T>) -> Work<T> { - let p = @mut Prep { - ctxt: self, - fn_name: fn_name.to_owned(), - declared_inputs: WorkMap::new() - }; - blk(p) - } -} - - -trait TPrep { - fn declare_input(&mut self, kind:&str, name:&str, val:&str); - fn is_fresh(&self, cat:&str, kind:&str, name:&str, val:&str) -> bool; - fn all_fresh(&self, cat:&str, map:&WorkMap) -> bool; - fn exec<T:Owned + - Encodable<json::Encoder> + - Decodable<json::Decoder>>( // FIXME(#5121) - &self, blk: ~fn(&Exec) -> T) -> Work<T>; -} - -impl TPrep for Prep { - fn declare_input(&mut self, kind:&str, name:&str, val:&str) { - self.declared_inputs.insert(WorkKey::new(kind, name), - val.to_owned()); - } - - fn is_fresh(&self, cat: &str, kind: &str, - name: &str, val: &str) -> bool { - let k = kind.to_owned(); - let f = (*self.ctxt.freshness.get(&k))(name, val); - let lg = self.ctxt.logger; - if f { - lg.info(fmt!("%s %s:%s is fresh", - cat, kind, name)); - } else { - lg.info(fmt!("%s %s:%s is not fresh", - cat, kind, name)) - } - f - } - - fn all_fresh(&self, cat: &str, map: &WorkMap) -> bool { - for map.each |k, v| { - if ! self.is_fresh(cat, k.kind, k.name, *v) { - return false; - } - } - return true; - } - - fn exec<T:Owned + - Encodable<json::Encoder> + - Decodable<json::Decoder>>( // FIXME(#5121) - &self, blk: ~fn(&Exec) -> T) -> Work<T> { - let mut bo = Some(blk); - - let cached = self.ctxt.db.prepare(self.fn_name, &self.declared_inputs); - - match cached { - Some((ref disc_in, ref disc_out, ref res)) - if self.all_fresh("declared input", - &self.declared_inputs) && - self.all_fresh("discovered input", disc_in) && - self.all_fresh("discovered output", disc_out) => { - Work::new(@mut copy *self, Left(json_decode(*res))) - } - - _ => { - let (port, chan) = oneshot(); - let blk = replace(&mut bo, None).unwrap(); - let chan = Cell(chan); - - do task::spawn { - let exe = Exec { - discovered_inputs: WorkMap::new(), - discovered_outputs: WorkMap::new(), - }; - let chan = chan.take(); - let v = blk(&exe); - send_one(chan, (exe, v)); - } - Work::new(@mut copy *self, Right(port)) - } - } - } -} - -pub impl<T:Owned + - Encodable<json::Encoder> + - Decodable<json::Decoder>> Work<T> { // FIXME(#5121) - fn new(p: @mut Prep, e: Either<T,PortOne<(Exec,T)>>) -> Work<T> { - Work { prep: p, res: Some(e) } - } -} - -// FIXME (#3724): movable self. This should be in impl Work. -fn unwrap<T:Owned + - Encodable<json::Encoder> + - Decodable<json::Decoder>>( // FIXME(#5121) - w: Work<T>) -> T { - let mut ww = w; - let s = replace(&mut ww.res, None); - - match s { - None => fail!(), - Some(Left(v)) => v, - Some(Right(port)) => { - let (exe, v) = recv_one(port); - - let s = json_encode(&v); - - let p = &*ww.prep; - let db = p.ctxt.db; - db.cache(p.fn_name, - &p.declared_inputs, - &exe.discovered_inputs, - &exe.discovered_outputs, - s); - v - } - } -} - -//#[test] -fn test() { - use core::io::WriterUtil; - - let db = @mut Database { db_filename: Path("db.json"), - db_cache: HashMap::new(), - db_dirty: false }; - let lg = @mut Logger { a: () }; - let cfg = @HashMap::new(); - let cx = @Context::new(db, lg, cfg); - let w:Work<~str> = do cx.prep("test1") |prep| { - let pth = Path("foo.c"); - { - let file = io::file_writer(&pth, [io::Create]).get(); - file.write_str("int main() { return 0; }"); - } - - prep.declare_input("file", pth.to_str(), digest_file(&pth)); - do prep.exec |_exe| { - let out = Path("foo.o"); - run::run_program("gcc", [~"foo.c", ~"-o", out.to_str()]); - out.to_str() - } - }; - let s = unwrap(w); - io::println(s); -} |
