From aa5b5ab886475b91ba86a3762e46d0cda2abf2ad Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Tue, 17 Jul 2012 16:31:19 -0700 Subject: [PATCH] Create some infrastructure for building up @-vectors. Work on #2921. --- src/libcore/at_vec.rs | 192 +++++++++++++++++++++++++++ src/libcore/core.rc | 3 +- src/libcore/vec.rs | 58 ++++++-- src/rt/boxed_region.cpp | 20 +++ src/rt/boxed_region.h | 1 + src/rt/rust_builtin.cpp | 8 ++ src/rt/rust_util.h | 10 ++ src/rt/rustrt.def.in | 1 + src/test/run-pass/at_vec_building.rs | 16 +++ 9 files changed, 295 insertions(+), 14 deletions(-) create mode 100644 src/libcore/at_vec.rs create mode 100644 src/test/run-pass/at_vec_building.rs diff --git a/src/libcore/at_vec.rs b/src/libcore/at_vec.rs new file mode 100644 index 00000000000..fc3355a03dd --- /dev/null +++ b/src/libcore/at_vec.rs @@ -0,0 +1,192 @@ +//! Shared Vectors + +import ptr::addr_of; + +export init_op; +export capacity; +export build_sized, build; +export map; +export from_fn, from_elem; +export unsafe; + +/// Code for dealing with @-vectors. This is pretty incomplete, and +/// contains a bunch of duplication from the code for ~-vectors. + +#[abi = "cdecl"] +extern mod rustrt { + fn vec_reserve_shared_actual(++t: *sys::type_desc, + ++v: **vec::unsafe::vec_repr, + ++n: libc::size_t); +} + +#[abi = "rust-intrinsic"] +extern mod rusti { + fn move_val_init(&dst: T, -src: T); +} + +/// A function used to initialize the elements of a vector +type init_op = fn(uint) -> T; + +/// Returns the number of elements the vector can hold without reallocating +#[inline(always)] +pure fn capacity(&&v: @[const T]) -> uint { + unsafe { + let repr: **unsafe::vec_repr = + ::unsafe::reinterpret_cast(addr_of(v)); + (**repr).alloc / sys::size_of::() + } +} + +/** + * 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 recieves + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pure fn build_sized(size: uint, builder: fn(push: pure fn(+A))) -> @[A] { + let mut vec = @[]; + unsafe { + unsafe::reserve(vec, size); + // This is an awful hack to be able to make the push function + // pure. Is there a better way? + ::unsafe::reinterpret_cast:: + + (builder)(|+x| unsafe::push(vec, x)); + } + ret 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 recieves + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pure fn build(builder: fn(push: pure fn(+A))) -> @[A] { + build_sized(4, builder) +} + +/// Apply a function to each element of a vector and return the results +pure fn map(v: &[T], f: fn(T) -> U) -> @[U] { + do build_sized(v.len()) |push| { + for vec::each(v) |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`. + */ +pure fn from_fn(n_elts: uint, op: init_op) -> @[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`. + */ +pure fn from_elem(n_elts: uint, t: T) -> @[T] { + do build_sized(n_elts) |push| { + let mut i: uint = 0u; + while i < n_elts { push(t); i += 1u; } + } +} + + +mod unsafe { + type vec_repr = vec::unsafe::vec_repr; + type slice_repr = vec::unsafe::slice_repr; + + /** + * 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)] + unsafe fn set_len(&&v: @[const T], new_len: uint) { + let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); + (**repr).fill = new_len * sys::size_of::(); + } + + /// Append an element to a vector + #[inline(always)] + unsafe fn push(&v: @[const T], +initval: T) { + let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); + let fill = (**repr).fill; + if (**repr).alloc > fill { + (**repr).fill += sys::size_of::(); + let p = addr_of((**repr).data); + let p = ptr::offset(p, fill) as *mut T; + rusti::move_val_init(*p, initval); + } + else { + push_slow(v, initval); + } + } + unsafe fn push_slow(&v: @[const T], +initval: T) { + reserve_at_least(v, v.len() + 1u); + push(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 + */ + unsafe fn reserve(&v: @[const T], n: uint) { + // Only make the (slow) call into the runtime if we have to + if capacity(v) < n { + let ptr = addr_of(v) as **vec_repr; + rustrt::vec_reserve_shared_actual(sys::get_type_desc::(), + 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 + */ + unsafe fn reserve_at_least(&v: @[const T], n: uint) { + reserve(v, uint::next_power_of_two(n)); + } + +} diff --git a/src/libcore/core.rc b/src/libcore/core.rc index ef336b3fa37..a90758694f8 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -36,7 +36,7 @@ export int, i8, i16, i32, i64; export uint, u8, u16, u32, u64; export float, f32, f64; -export box, char, str, ptr, vec, bool; +export box, char, str, ptr, vec, at_vec, bool; export either, option, result, iter; export libc, os, io, run, rand, sys, unsafe, logging; export arc, comm, task, future, pipes; @@ -150,6 +150,7 @@ mod f64; mod str; mod ptr; mod vec; +mod at_vec; mod bool; mod tuple; diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 1dd4ef5ae43..e21fd1a20a3 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -17,6 +17,7 @@ export capacity; export len; export from_fn; export from_elem; +export build, build_sized; export to_mut; export from_mut; export head; @@ -211,6 +212,47 @@ pure fn from_elem(n_elts: uint, t: T) -> ~[T] { ret v; } +/** + * 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 recieves + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pure fn build_sized(size: uint, builder: fn(push: pure fn(+A))) -> ~[A] { + let mut vec = ~[]; + unsafe { + reserve(vec, size); + // This is an awful hack to be able to make the push function + // pure. Is there a better way? + ::unsafe::reinterpret_cast:: + + (builder)(|+x| push(vec, x)); + } + ret 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 recieves + * as an argument a function that will push an element + * onto the vector being constructed. + */ +#[inline(always)] +pure fn build(builder: fn(push: pure fn(+A))) -> ~[A] { + build_sized(4, builder) +} + /// Produces a mut vector from an immutable vector. pure fn to_mut(+v: ~[T]) -> ~[mut T] { unsafe { ::unsafe::transmute(v) } @@ -444,8 +486,7 @@ fn push(&v: ~[const T], +initval: T) { let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); let fill = (**repr).fill; if (**repr).alloc > fill { - let sz = sys::size_of::(); - (**repr).fill += sz; + (**repr).fill += sys::size_of::(); let p = ptr::addr_of((**repr).data); let p = ptr::offset(p, fill) as *mut T; rusti::move_val_init(*p, initval); @@ -457,17 +498,8 @@ fn push(&v: ~[const T], +initval: T) { } fn push_slow(&v: ~[const T], +initval: T) { - unsafe { - let ln = v.len(); - reserve_at_least(v, ln + 1u); - let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); - let fill = (**repr).fill; - let sz = sys::size_of::(); - (**repr).fill += sz; - let p = ptr::addr_of((**repr).data); - let p = ptr::offset(p, fill) as *mut T; - rusti::move_val_init(*p, initval); - } + reserve_at_least(v, v.len() + 1u); + push(v, initval); } // Unchecked vector indexing diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp index 6b04028c3fc..60cc9edec8d 100644 --- a/src/rt/boxed_region.cpp +++ b/src/rt/boxed_region.cpp @@ -26,6 +26,26 @@ rust_opaque_box *boxed_region::malloc(type_desc *td, size_t body_size) { return box; } +rust_opaque_box *boxed_region::realloc(rust_opaque_box *box, + size_t new_size) { + assert(box->ref_count == 1); + + size_t total_size = new_size + sizeof(rust_opaque_box); + rust_opaque_box *new_box = + (rust_opaque_box*)backing_region->realloc(box, total_size); + if (new_box->prev) new_box->prev->next = new_box; + if (new_box->next) new_box->next->prev = new_box; + if (live_allocs == box) live_allocs = new_box; + + + LOG(rust_get_current_task(), box, + "@realloc()=%p with orig=%p, size %lu==%lu+%lu", + new_box, box, total_size, sizeof(rust_opaque_box), new_size); + + return new_box; +} + + rust_opaque_box *boxed_region::calloc(type_desc *td, size_t body_size) { rust_opaque_box *box = malloc(td, body_size); memset(box_body(box), 0, td->size); diff --git a/src/rt/boxed_region.h b/src/rt/boxed_region.h index 17282d3e5d5..cb01335a1bc 100644 --- a/src/rt/boxed_region.h +++ b/src/rt/boxed_region.h @@ -36,6 +36,7 @@ public: rust_opaque_box *malloc(type_desc *td, size_t body_size); rust_opaque_box *calloc(type_desc *td, size_t body_size); + rust_opaque_box *realloc(rust_opaque_box *box, size_t new_size); void free(rust_opaque_box *box); }; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 001fcd6198b..4975bbf0354 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -133,6 +133,14 @@ unsupervise() { task->unsupervise(); } +extern "C" CDECL void +vec_reserve_shared_actual(type_desc* ty, rust_vec_box** vp, + size_t n_elts) { + rust_task *task = rust_get_current_task(); + reserve_vec_exact_shared(task, vp, n_elts * ty->size); +} + +// This is completely misnamed. extern "C" CDECL void vec_reserve_shared(type_desc* ty, rust_vec_box** vp, size_t n_elts) { diff --git a/src/rt/rust_util.h b/src/rt/rust_util.h index 247f253fd9f..e2c75d0cae4 100644 --- a/src/rt/rust_util.h +++ b/src/rt/rust_util.h @@ -62,6 +62,16 @@ vec_data(rust_vec *v) { return reinterpret_cast(v->data); } +inline void reserve_vec_exact_shared(rust_task* task, rust_vec_box** vpp, + size_t size) { + rust_opaque_box** ovpp = (rust_opaque_box**)vpp; + if (size > (*vpp)->body.alloc) { + *vpp = (rust_vec_box*)task->boxed.realloc( + *ovpp, size + sizeof(rust_vec)); + (*vpp)->body.alloc = size; + } +} + inline void reserve_vec_exact(rust_task* task, rust_vec_box** vpp, size_t size) { if (size > (*vpp)->body.alloc) { diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index a8256bba300..620e9199e01 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -61,6 +61,7 @@ rust_task_unweaken sched_threads shape_log_str start_task +vec_reserve_shared_actual vec_reserve_shared str_reserve_shared vec_from_buf_shared diff --git a/src/test/run-pass/at_vec_building.rs b/src/test/run-pass/at_vec_building.rs new file mode 100644 index 00000000000..3202dfe221a --- /dev/null +++ b/src/test/run-pass/at_vec_building.rs @@ -0,0 +1,16 @@ +import at_vec::{build, from_fn, from_elem}; + +// 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); + } + } +} + +fn main() { + assert seq_range(10, 15) == @[10, 11, 12, 13, 14]; + assert from_fn(5, |x| x+1) == @[1, 2, 3, 4, 5]; + assert from_elem(5, 3.14) == @[3.14, 3.14, 3.14, 3.14, 3.14]; +}