add swappable/dvec modules
This commit is contained in:
parent
253979236e
commit
da204e1d73
@ -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
237
src/libcore/dvec.rs
Normal 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 }
|
||||
}
|
||||
}
|
16
src/libcore/iter-trait/dvec.rs
Normal file
16
src/libcore/iter-trait/dvec.rs
Normal 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
98
src/libcore/swappable.rs
Normal 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()
|
||||
}
|
||||
}
|
53
src/test/bench/vec-append.rs
Normal file
53
src/test/bench/vec-append.rs
Normal 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));
|
||||
}
|
||||
}
|
26
src/test/run-pass/dvec-test.rs
Normal file
26
src/test/run-pass/dvec-test.rs
Normal 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];
|
||||
}
|
13
src/test/run-pass/swappable-test.rs
Normal file
13
src/test/run-pass/swappable-test.rs
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user