add swappable/dvec modules
This commit is contained in:
parent
253979236e
commit
da204e1d73
@ -41,6 +41,8 @@ export comm, task, future;
|
|||||||
export extfmt;
|
export extfmt;
|
||||||
export tuple;
|
export tuple;
|
||||||
export to_str;
|
export to_str;
|
||||||
|
export swappable;
|
||||||
|
export dvec, dvec_iter;
|
||||||
|
|
||||||
// NDM seems to be necessary for resolve to work
|
// NDM seems to be necessary for resolve to work
|
||||||
export vec_iter, option_iter;
|
export vec_iter, option_iter;
|
||||||
@ -165,7 +167,13 @@ mod option_iter {
|
|||||||
}
|
}
|
||||||
mod result;
|
mod result;
|
||||||
mod to_str;
|
mod to_str;
|
||||||
|
mod swappable;
|
||||||
|
mod dvec;
|
||||||
|
#[path="iter-trait"]
|
||||||
|
mod dvec_iter {
|
||||||
|
#[path = "dvec.rs"]
|
||||||
|
mod inst;
|
||||||
|
}
|
||||||
|
|
||||||
// Concurrency
|
// Concurrency
|
||||||
mod comm;
|
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