rust/src/libstd/c_vec.rs

188 lines
4.8 KiB
Rust
Raw Normal View History

/*!
* Library to interface with chunks of memory allocated in C.
*
* It is often desirable to safely interface with memory allocated from C,
* encapsulating the unsafety into allocation and destruction time. Indeed,
* allocating memory externally is currently the only way to give Rust shared
* mut state with C programs that keep their own references; vectors are
* unsuitable because they could be reallocated or moved at any time, and
* importing C memory into a vector takes a one-time snapshot of the memory.
*
* This module simplifies the usage of such external blocks of memory. Memory
* is encapsulated into an opaque object after creation; the lifecycle of the
* memory can be optionally managed by Rust, if an appropriate destructor
* closure is provided. Safety is ensured by bounds-checking accesses, which
* are marshalled through get and set functions.
*
* There are three unsafe functions: the two introduction forms, and the
* pointer elimination form. The introduction forms are unsafe for the
* obvious reason (they act on a pointer that cannot be checked inside the
* method), but the elimination form is somewhat more subtle in its unsafety.
* By using a pointer taken from a c_vec::t without keeping a reference to the
2012-08-11 10:08:42 -04:00
* c_vec::t itself around, the CVec could be garbage collected, and the
* memory within could be destroyed. There are legitimate uses for the
* pointer elimination form -- for instance, to pass memory back into C -- but
* great care must be taken to ensure that a reference to the c_vec::t is
* still held if needed.
*/
2011-11-23 05:15:04 -05:00
2012-08-11 10:08:42 -04:00
export CVec;
2012-08-29 14:45:25 -07:00
export CVec, c_vec_with_dtor;
2011-11-23 05:15:04 -05:00
export get, set;
2012-01-06 08:12:18 -08:00
export len;
2011-11-23 05:15:04 -05:00
export ptr;
/**
* The type representing a foreign chunk of memory
*
* Wrapped in a enum for opacity; FIXME #818 when it is possible to have
* truly opaque types, this should be revisited.
*/
2012-08-11 10:08:42 -04:00
enum CVec<T> {
CVecCtor({ base: *mut T, len: uint, rsrc: @DtorRes})
2011-11-23 05:15:04 -05:00
}
2012-08-11 10:08:42 -04:00
struct DtorRes {
2012-08-20 12:23:37 -07:00
let dtor: Option<fn@()>;
new(dtor: Option<fn@()>) { self.dtor = dtor; }
drop {
2012-08-06 12:34:08 -07:00
match self.dtor {
2012-08-20 12:23:37 -07:00
option::None => (),
option::Some(f) => f()
2011-11-23 05:15:04 -05:00
}
}
2011-11-23 05:15:04 -05:00
}
/*
Section: Introduction forms
*/
/**
2012-08-11 10:08:42 -04:00
* Create a `CVec` from a foreign buffer with a given length.
*
* # Arguments
*
* * base - A foreign pointer to a buffer
* * len - The number of elements in the buffer
*/
2012-08-29 14:45:25 -07:00
unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
2012-08-11 10:08:42 -04:00
return CVecCtor({
base: base,
len: len,
2012-08-11 10:08:42 -04:00
rsrc: @DtorRes(option::None)
});
2011-11-23 05:15:04 -05:00
}
/**
2012-08-11 10:08:42 -04:00
* Create a `CVec` from a foreign buffer, with a given length,
* and a function to run upon destruction.
*
* # Arguments
*
* * base - A foreign pointer to a buffer
* * len - The number of elements in the buffer
* * dtor - A function to run when the value is destructed, useful
* for freeing the buffer, etc.
*/
2012-03-26 18:35:18 -07:00
unsafe fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: fn@())
2012-08-11 10:08:42 -04:00
-> CVec<T> {
return CVecCtor({
base: base,
len: len,
2012-08-11 10:08:42 -04:00
rsrc: @DtorRes(option::Some(dtor))
});
2011-11-23 05:15:04 -05:00
}
/*
Section: Operations
*/
/**
* Retrieves an element at a given index
*
* Fails if `ofs` is greater or equal to the length of the vector
*/
2012-08-11 10:08:42 -04:00
fn get<T: copy>(t: CVec<T>, ofs: uint) -> T {
2012-01-06 08:12:18 -08:00
assert ofs < len(t);
2012-08-01 17:30:05 -07:00
return unsafe { *ptr::mut_offset((*t).base, ofs) };
2011-11-23 05:15:04 -05:00
}
/**
* Sets the value of an element at a given index
*
* Fails if `ofs` is greater or equal to the length of the vector
*/
2012-08-11 10:08:42 -04:00
fn set<T: copy>(t: CVec<T>, ofs: uint, v: T) {
2012-01-06 08:12:18 -08:00
assert ofs < len(t);
unsafe { *ptr::mut_offset((*t).base, ofs) = v };
2011-11-23 05:15:04 -05:00
}
/*
Section: Elimination forms
*/
/// Returns the length of the vector
2012-08-11 10:08:42 -04:00
fn len<T>(t: CVec<T>) -> uint {
2012-08-01 17:30:05 -07:00
return (*t).len;
2011-11-23 05:15:04 -05:00
}
/// Returns a pointer to the first element of the vector
2012-08-11 10:08:42 -04:00
unsafe fn ptr<T>(t: CVec<T>) -> *mut T {
2012-08-01 17:30:05 -07:00
return (*t).base;
2011-11-23 05:15:04 -05:00
}
2012-01-17 19:05:07 -08:00
#[cfg(test)]
mod tests {
import libc::*;
2012-01-17 19:05:07 -08:00
2012-08-11 10:08:42 -04:00
fn malloc(n: size_t) -> CVec<u8> {
2012-01-17 19:05:07 -08:00
let mem = libc::malloc(n);
assert mem as int != 0;
2012-08-01 17:30:05 -07:00
return unsafe { c_vec_with_dtor(mem as *mut u8, n as uint,
2012-06-30 16:19:07 -07:00
||free(mem)) };
2012-01-17 19:05:07 -08:00
}
#[test]
fn test_basic() {
let cv = malloc(16u as size_t);
2012-01-17 19:05:07 -08:00
set(cv, 3u, 8u8);
set(cv, 4u, 9u8);
assert get(cv, 3u) == 8u8;
assert get(cv, 4u) == 9u8;
assert len(cv) == 16u;
}
#[test]
#[should_fail]
#[ignore(cfg(windows))]
2012-01-17 19:05:07 -08:00
fn test_overrun_get() {
let cv = malloc(16u as size_t);
2012-01-17 19:05:07 -08:00
get(cv, 17u);
}
#[test]
#[should_fail]
#[ignore(cfg(windows))]
2012-01-17 19:05:07 -08:00
fn test_overrun_set() {
let cv = malloc(16u as size_t);
2012-01-17 19:05:07 -08:00
set(cv, 17u, 0u8);
}
#[test]
fn test_and_I_mean_it() {
let cv = malloc(16u as size_t);
2012-01-17 19:05:07 -08:00
let p = unsafe { ptr(cv) };
set(cv, 0u, 32u8);
set(cv, 1u, 33u8);
assert unsafe { *p } == 32u8;
set(cv, 2u, 34u8); /* safety */
}
}