Create some infrastructure for building up @-vectors. Work on #2921.
This commit is contained in:
192
src/libcore/at_vec.rs
Normal file
192
src/libcore/at_vec.rs
Normal file
@@ -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<T>(&dst: T, -src: T);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function used to initialize the elements of a vector
|
||||||
|
type init_op<T> = fn(uint) -> T;
|
||||||
|
|
||||||
|
/// Returns the number of elements the vector can hold without reallocating
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn capacity<T>(&&v: @[const T]) -> uint {
|
||||||
|
unsafe {
|
||||||
|
let repr: **unsafe::vec_repr =
|
||||||
|
::unsafe::reinterpret_cast(addr_of(v));
|
||||||
|
(**repr).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 recieves
|
||||||
|
* as an argument a function that will push an element
|
||||||
|
* onto the vector being constructed.
|
||||||
|
*/
|
||||||
|
#[inline(always)]
|
||||||
|
pure fn build_sized<A>(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::
|
||||||
|
<fn(push: pure fn(+A)), fn(push: fn(+A))>
|
||||||
|
(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<A>(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<T, U>(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<T>(n_elts: uint, op: init_op<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`.
|
||||||
|
*/
|
||||||
|
pure 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(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<T>(&&v: @[const T], new_len: uint) {
|
||||||
|
let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
|
||||||
|
(**repr).fill = new_len * sys::size_of::<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append an element to a vector
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn push<T>(&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::<T>();
|
||||||
|
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<T>(&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<T>(&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::<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
|
||||||
|
*/
|
||||||
|
unsafe fn reserve_at_least<T>(&v: @[const T], n: uint) {
|
||||||
|
reserve(v, uint::next_power_of_two(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
export int, i8, i16, i32, i64;
|
export int, i8, i16, i32, i64;
|
||||||
export uint, u8, u16, u32, u64;
|
export uint, u8, u16, u32, u64;
|
||||||
export float, f32, f64;
|
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 either, option, result, iter;
|
||||||
export libc, os, io, run, rand, sys, unsafe, logging;
|
export libc, os, io, run, rand, sys, unsafe, logging;
|
||||||
export arc, comm, task, future, pipes;
|
export arc, comm, task, future, pipes;
|
||||||
@@ -150,6 +150,7 @@ mod f64;
|
|||||||
mod str;
|
mod str;
|
||||||
mod ptr;
|
mod ptr;
|
||||||
mod vec;
|
mod vec;
|
||||||
|
mod at_vec;
|
||||||
mod bool;
|
mod bool;
|
||||||
mod tuple;
|
mod tuple;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export capacity;
|
|||||||
export len;
|
export len;
|
||||||
export from_fn;
|
export from_fn;
|
||||||
export from_elem;
|
export from_elem;
|
||||||
|
export build, build_sized;
|
||||||
export to_mut;
|
export to_mut;
|
||||||
export from_mut;
|
export from_mut;
|
||||||
export head;
|
export head;
|
||||||
@@ -211,6 +212,47 @@ pure fn from_elem<T: copy>(n_elts: uint, t: T) -> ~[T] {
|
|||||||
ret v;
|
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<A>(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::
|
||||||
|
<fn(push: pure fn(+A)), fn(push: fn(+A))>
|
||||||
|
(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<A>(builder: fn(push: pure fn(+A))) -> ~[A] {
|
||||||
|
build_sized(4, builder)
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a mut vector from an immutable vector.
|
/// Produces a mut vector from an immutable vector.
|
||||||
pure fn to_mut<T>(+v: ~[T]) -> ~[mut T] {
|
pure fn to_mut<T>(+v: ~[T]) -> ~[mut T] {
|
||||||
unsafe { ::unsafe::transmute(v) }
|
unsafe { ::unsafe::transmute(v) }
|
||||||
@@ -444,8 +486,7 @@ fn push<T>(&v: ~[const T], +initval: T) {
|
|||||||
let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
|
let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
|
||||||
let fill = (**repr).fill;
|
let fill = (**repr).fill;
|
||||||
if (**repr).alloc > fill {
|
if (**repr).alloc > fill {
|
||||||
let sz = sys::size_of::<T>();
|
(**repr).fill += sys::size_of::<T>();
|
||||||
(**repr).fill += sz;
|
|
||||||
let p = ptr::addr_of((**repr).data);
|
let p = ptr::addr_of((**repr).data);
|
||||||
let p = ptr::offset(p, fill) as *mut T;
|
let p = ptr::offset(p, fill) as *mut T;
|
||||||
rusti::move_val_init(*p, initval);
|
rusti::move_val_init(*p, initval);
|
||||||
@@ -457,17 +498,8 @@ fn push<T>(&v: ~[const T], +initval: T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_slow<T>(&v: ~[const T], +initval: T) {
|
fn push_slow<T>(&v: ~[const T], +initval: T) {
|
||||||
unsafe {
|
reserve_at_least(v, v.len() + 1u);
|
||||||
let ln = v.len();
|
push(v, initval);
|
||||||
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::<T>();
|
|
||||||
(**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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unchecked vector indexing
|
// Unchecked vector indexing
|
||||||
|
|||||||
@@ -26,6 +26,26 @@ rust_opaque_box *boxed_region::malloc(type_desc *td, size_t body_size) {
|
|||||||
return box;
|
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 *boxed_region::calloc(type_desc *td, size_t body_size) {
|
||||||
rust_opaque_box *box = malloc(td, body_size);
|
rust_opaque_box *box = malloc(td, body_size);
|
||||||
memset(box_body(box), 0, td->size);
|
memset(box_body(box), 0, td->size);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public:
|
|||||||
|
|
||||||
rust_opaque_box *malloc(type_desc *td, size_t body_size);
|
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 *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);
|
void free(rust_opaque_box *box);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,14 @@ unsupervise() {
|
|||||||
task->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
|
extern "C" CDECL void
|
||||||
vec_reserve_shared(type_desc* ty, rust_vec_box** vp,
|
vec_reserve_shared(type_desc* ty, rust_vec_box** vp,
|
||||||
size_t n_elts) {
|
size_t n_elts) {
|
||||||
|
|||||||
@@ -62,6 +62,16 @@ vec_data(rust_vec *v) {
|
|||||||
return reinterpret_cast<T*>(v->data);
|
return reinterpret_cast<T*>(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,
|
inline void reserve_vec_exact(rust_task* task, rust_vec_box** vpp,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
if (size > (*vpp)->body.alloc) {
|
if (size > (*vpp)->body.alloc) {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ rust_task_unweaken
|
|||||||
sched_threads
|
sched_threads
|
||||||
shape_log_str
|
shape_log_str
|
||||||
start_task
|
start_task
|
||||||
|
vec_reserve_shared_actual
|
||||||
vec_reserve_shared
|
vec_reserve_shared
|
||||||
str_reserve_shared
|
str_reserve_shared
|
||||||
vec_from_buf_shared
|
vec_from_buf_shared
|
||||||
|
|||||||
16
src/test/run-pass/at_vec_building.rs
Normal file
16
src/test/run-pass/at_vec_building.rs
Normal file
@@ -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];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user