add swappable/dvec modules

This commit is contained in:
Niko Matsakis 2012-05-09 17:30:31 -07:00
parent 253979236e
commit da204e1d73
7 changed files with 452 additions and 1 deletions

View File

@ -41,6 +41,8 @@ export comm, task, future;
export extfmt;
export tuple;
export to_str;
export swappable;
export dvec, dvec_iter;
// NDM seems to be necessary for resolve to work
export vec_iter, option_iter;
@ -165,7 +167,13 @@ mod option_iter {
}
mod result;
mod to_str;
mod swappable;
mod dvec;
#[path="iter-trait"]
mod dvec_iter {
#[path = "dvec.rs"]
mod inst;
}
// Concurrency
mod comm;

237
src/libcore/dvec.rs Normal file
View File

@ -0,0 +1,237 @@
// Dynamic Vector
//
// A growable vector that makes use of unique pointers so that the
// result can be sent between tasks and so forth.
//
// Note that recursive use is not permitted.
import dvec_iter::extensions;
import unsafe::reinterpret_cast;
import ptr::{null, extensions};
export dvec;
export from_vec;
export extensions;
export unwrap;
#[doc = "
A growable, modifiable vector type that accumulates elements into a
unique vector.
# Limitations on recursive use
This class works by swapping the unique vector out of the data
structure whenever it is to be used. Therefore, recursive use is not
permitted. That is, while iterating through a vector, you cannot
access the vector in any other way or else the program will fail. If
you wish, you can use the `swap()` method to gain access to the raw
vector and transform it or use it any way you like. Eventually, we
may permit read-only access during iteration or other use.
# WARNING
For maximum performance, this type is implemented using some rather
unsafe code. In particular, this innocent looking `[mut A]` pointer
*may be null!* Therefore, it is important you not reach into the
data structure manually but instead use the provided extensions.
The reason that I did not use an unsafe pointer in the structure
itself is that I wanted to ensure that the vector would be freed when
the dvec is dropped. The reason that I did not use an `option<T>`
instead of a nullable pointer is that I found experimentally that it
becomes approximately 50% slower. This can probably be improved
through optimization. You can run your own experiments using
`src/test/bench/vec-append.rs`. My own tests found that using null
pointers achieved about 103 million pushes/second. Using an option
type could only produce 47 million pushes/second.
"]
type dvec<A> = {
mut data: [mut A]
};
#[doc = "Creates a new, empty dvec"]
fn dvec<A>() -> dvec<A> {
{mut data: [mut]}
}
#[doc = "Creates a new dvec with the contents of a vector"]
fn from_vec<A>(+v: [mut A]) -> dvec<A> {
{mut data: v}
}
#[doc = "Consumes the vector and returns its contents"]
fn unwrap<A>(-d: dvec<A>) -> [mut A] {
let {data: v} <- d;
ret v;
}
impl private_methods<A> for dvec<A> {
fn check_not_borrowed() {
unsafe {
let data: *() = unsafe::reinterpret_cast(self.data);
if data.is_null() {
fail "Recursive use of dvec";
}
}
}
#[inline(always)]
fn borrow<B>(f: fn(-[mut A]) -> B) -> B {
unsafe {
let mut data = unsafe::reinterpret_cast(null::<()>());
data <-> self.data;
let data_ptr: *() = unsafe::reinterpret_cast(data);
if data_ptr.is_null() { fail "Recursive use of dvec"; }
ret f(data);
}
}
#[inline(always)]
fn return(-data: [mut A]) {
unsafe {
self.data <- data;
}
}
}
// In theory, most everything should work with any A, but in practice
// almost nothing works without the copy bound due to limitations
// around closures.
impl extensions<A> for dvec<A> {
#[doc = "
Swaps out the current vector and hands it off to a user-provided
function `f`. The function should transform it however is desired
and return a new vector to replace it with.
"]
fn swap(f: fn(-[mut A]) -> [mut A]) {
self.borrow { |v| self.return(f(v)) }
}
#[doc = "Returns the number of elements currently in the dvec"]
fn len() -> uint {
self.borrow { |v|
let l = v.len();
self.return(v);
l
}
}
#[doc = "Overwrite the current contents"]
fn set(+w: [mut A]) {
self.check_not_borrowed();
self.data <- w; //FIXME check for recursive use
}
}
impl extensions<A:copy> for dvec<A> {
#[doc = "Append a single item to the end of the list"]
fn push(t: A) {
self.swap { |v| v += [t]; v } // more efficient than v + [t]
}
#[doc = "Remove and return the last element"]
fn pop() -> A {
self.borrow { |v|
let result = vec::pop(v);
self.return(v);
result
}
}
#[doc = "
Append all elements of a vector to the end of the list
Equivalent to `append_iter()` but potentially more efficient.
"]
fn push_all(ts: [const A]/&) {
self.push_slice(ts, 0u, vec::len(ts));
}
#[doc = "
Appends elements from `from_idx` to `to_idx` (exclusive)
"]
fn push_slice(ts: [const A]/&, from_idx: uint, to_idx: uint) {
self.swap { |v|
let new_len = vec::len(v) + to_idx - from_idx;
vec::reserve(v, new_len);
let mut i = from_idx;
while i < to_idx {
v += [ts[i]];
i += 1u;
}
v
}
}
//FIXME--
//#[doc = "
// Append all elements of an iterable.
//
// Failure will occur if the iterable's `each()` method
// attempts to access this vector.
//"]
//fn append_iter<I:iter::base<A>>(ts: I) {
// self.dvec.swap { |v|
// alt ts.size_hint() {
// none {}
// some(h) { vec::reserve(v, len(v) + h) }
// }
//
// for ts.each { |t| v = v + [t] };
//
// v
// }
//}
#[doc = "
Gets a copy of the current contents.
See `unwrap()` if you do not wish to copy the contents.
"]
fn get() -> [A] {
self.borrow { |v|
let w = vec::from_mut(copy v);
self.return(v);
w
}
}
#[doc = "Remove and return the first element"]
fn shift() -> A {
self.borrow { |v|
let mut v = vec::from_mut(v);
let result = vec::shift(v);
self.return(vec::to_mut(v));
result
}
}
#[doc = "Copy out an individual element"]
#[inline]
fn [](idx: uint) -> A {
self.get_elt(idx)
}
#[doc = "Copy out an individual element"]
#[inline]
fn get_elt(idx: uint) -> A {
self.check_not_borrowed();
ret self.data[idx];
}
#[doc = "Overwrites the contents of the element at `idx` with `a`"]
fn set_elt(idx: uint, a: A) {
self.check_not_borrowed();
self.data[idx] = a;
}
#[doc = "Overwrites the contents of the element at `idx` with `a`"]
fn grow_set_elt(idx: uint, initval: A, val: A) {
self.swap { |v| vec::grow_set(v, idx, initval, val); v }
}
}

View File

@ -0,0 +1,16 @@
type IMPL_T<A> = dvec::dvec<A>;
#[doc = "
Iterates through the current contents.
Attempts to access this dvec during iteration will fail.
"]
fn EACH<A>(self: IMPL_T<A>, f: fn(A) -> bool) {
import dvec::extensions;
self.swap { |v| vec::each(v, f); v }
}
fn SIZE_HINT<A>(self: IMPL_T<A>) -> option<uint> {
import dvec::extensions;
some(self.len())
}

98
src/libcore/swappable.rs Normal file
View File

@ -0,0 +1,98 @@
export swappable;
export unwrap;
export methods;
#[doc = "
A value that may be swapped out temporarily while it is being processed
and then replaced. Swappables are most useful when working with unique
values, which often cannot be mutated unless they are stored in the local
stack frame to ensure memory safety.
The type guarantees the invariant that the value is always \"swapped in\"
except during the execution of the `swap()` and `with()` methods.
"]
type swappable<A> = {
mut o_t: option<A>
};
#[doc = "Create a swappable swapped in with a given initial value"]
fn swappable<A>(+t: A) -> swappable<A> {
{mut o_t: some(t)}
}
#[doc = "Consumes a swappable and returns its contents without copying"]
fn unwrap<A>(-s: swappable<A>) -> A {
let {o_t: o_t} <- s;
option::unwrap(o_t)
}
impl methods<A> for swappable<A> {
#[doc = "
Overwrites the contents of the swappable
"]
fn set(+a: A) {
self.o_t <- some(a);
}
#[doc = "
Invokes `f()` with the current value but replaces the
current value when complete. Returns the result of `f()`.
Attempts to read or access the receiver while `f()` is executing
will fail dynamically.
"]
fn with<B>(f: fn(A) -> B) -> B {
let mut o_u = none;
self.swap { |t| o_u <- some(f(t)); t }
option::unwrap(o_u)
}
#[doc = "
Invokes `f()` with the current value and then replaces the
current value with the result of `f()`.
Attempts to read or access the receiver while `f()` is executing
will fail dynamically.
"]
fn swap(f: fn(-A) -> A) {
alt self.o_t {
none { fail "no value present---already swapped?"; }
some(_) {}
}
let mut o_t = none;
o_t <-> self.o_t;
self.o_t <- some(f(option::unwrap(o_t)));
}
#[doc = "True if there is a value present in this swappable"]
fn is_present() -> bool {
alt self.o_t {
none {false}
some(_) {true}
}
}
#[doc = "
Removes the value from the swappable. Any further attempts
to use the swapabble without first invoking `set()` will fail.
"]
fn take() -> A {
alt self.o_t {
none { fail "swapped out"; }
some(_) {}
}
let mut o_t = none;
option::unwrap(o_t)
}
}
impl methods<A:copy> for swappable<A> {
#[doc = "
Copies out the contents of the swappable
"]
fn get() -> A {
self.o_t.get()
}
}

View File

@ -0,0 +1,53 @@
// A raw test of vector appending performance.
use std;
import dvec::{dvec, extensions};
import io::writer_util;
fn collect_raw(num: uint) -> [uint] {
let mut result = [];
uint::range(0u, num) { |i|
result += [i];
}
ret result;
}
fn collect_dvec(num: uint) -> [mut uint] {
let result = dvec();
uint::range(0u, num) { |i|
result.push(i);
}
ret dvec::unwrap(result);
}
fn main(args: [str]) {
let args = if vec::len(args) <= 1u {["", "100000"]} else {args};
let max = uint::from_str(args[1]).get();
let start = std::time::precise_time_s();
collect_raw(max);
let mid = std::time::precise_time_s();
collect_dvec(max);
let end = std::time::precise_time_s();
let raw = mid - start;
let dvec = end - mid;
let maxf = max as float;
let rawf = raw as float;
let dvecf = dvec as float;
io::stdout().write_str(#fmt("Raw : %? seconds\n", raw));
io::stdout().write_str(#fmt(" : %f op/sec\n", maxf/rawf));
io::stdout().write_str(#fmt("\n"));
io::stdout().write_str(#fmt("Dvec : %? seconds\n", dvec));
io::stdout().write_str(#fmt(" : %f op/sec\n", maxf/dvecf));
io::stdout().write_str(#fmt("\n"));
if dvec < raw {
io::stdout().write_str(#fmt("Dvec is %f%% faster than raw\n",
(rawf - dvecf) / rawf * 100.0));
} else {
io::stdout().write_str(#fmt("Raw is %f%% faster than dvec\n",
(dvecf - rawf) / dvecf * 100.0));
}
}

View File

@ -0,0 +1,26 @@
import dvec::{dvec, extensions};
fn main() {
let d = dvec();
d.push(3);
d.push(4);
assert d.get() == [3, 4];
d.set([mut 5]);
d.push(6);
d.push(7);
d.push(8);
d.push(9);
d.push(10);
d.push_vec([11, 12, 13]);
let exp = [5, 6, 7, 8, 9, 10, 11, 12, 13];
assert d.get() == exp;
assert d.get() == exp;
assert d.len() == exp.len();
for d.eachi { |i, e|
assert e == exp[i];
}
assert dvec::unwrap(d) == [5, 6, 7, 8, 9, 10, 11, 12, 13];
}

View File

@ -0,0 +1,13 @@
import swappable::{swappable, methods};
fn main() {
let d = swappable(3);
assert d.get() == 3;
d.set(4);
assert d.get() == 4;
d.swap { |i| i + 1 };
assert d.get() == 5;
assert d.with { |i| i + 1 } == 6;
assert d.get() == 5;
assert swappable::unwrap(d) == 5;
}