211 lines
4.8 KiB
Rust
211 lines
4.8 KiB
Rust
/*
|
|
Module: c_vec
|
|
|
|
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
|
|
mutable 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 c_vec::t
|
|
itself around, the c_vec 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.
|
|
|
|
*/
|
|
|
|
export t;
|
|
export create, create_with_dtor;
|
|
export get, set;
|
|
export len;
|
|
export ptr;
|
|
|
|
/*
|
|
Type: t
|
|
|
|
The type representing a native chunk of memory. Wrapped in a enum for
|
|
opacity; FIXME #818 when it is possible to have truly opaque types, this
|
|
should be revisited.
|
|
*/
|
|
|
|
enum t<T> {
|
|
t({ base: *mutable T, len: uint, rsrc: @dtor_res})
|
|
}
|
|
|
|
resource dtor_res(dtor: option::t<fn@()>) {
|
|
alt dtor {
|
|
option::none { }
|
|
option::some(f) { f(); }
|
|
}
|
|
}
|
|
|
|
/*
|
|
Section: Introduction forms
|
|
*/
|
|
|
|
/*
|
|
Function: create
|
|
|
|
Create a c_vec::t from a native buffer with a given length.
|
|
|
|
Parameters:
|
|
|
|
base - A native pointer to a buffer
|
|
len - The number of elements in the buffer
|
|
*/
|
|
unsafe fn create<T>(base: *mutable T, len: uint) -> t<T> {
|
|
ret t({base: base,
|
|
len: len,
|
|
rsrc: @dtor_res(option::none)
|
|
});
|
|
}
|
|
|
|
/*
|
|
Function: create_with_dtor
|
|
|
|
Create a c_vec::t from a native buffer, with a given length,
|
|
and a function to run upon destruction.
|
|
|
|
Parameters:
|
|
|
|
base - A native 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.
|
|
*/
|
|
unsafe fn create_with_dtor<T>(base: *mutable T, len: uint, dtor: fn@())
|
|
-> t<T> {
|
|
ret t({base: base,
|
|
len: len,
|
|
rsrc: @dtor_res(option::some(dtor))
|
|
});
|
|
}
|
|
|
|
/*
|
|
Section: Operations
|
|
*/
|
|
|
|
/*
|
|
Function: get
|
|
|
|
Retrieves an element at a given index
|
|
|
|
Failure:
|
|
|
|
If `ofs` is greater or equal to the length of the vector
|
|
*/
|
|
fn get<T: copy>(t: t<T>, ofs: uint) -> T {
|
|
assert ofs < len(t);
|
|
ret unsafe { *ptr::mut_offset((*t).base, ofs) };
|
|
}
|
|
|
|
/*
|
|
Function: set
|
|
|
|
Sets the value of an element at a given index
|
|
|
|
Failure:
|
|
|
|
If `ofs` is greater or equal to the length of the vector
|
|
*/
|
|
fn set<T: copy>(t: t<T>, ofs: uint, v: T) {
|
|
assert ofs < len(t);
|
|
unsafe { *ptr::mut_offset((*t).base, ofs) = v };
|
|
}
|
|
|
|
/*
|
|
Section: Elimination forms
|
|
*/
|
|
|
|
/*
|
|
Function: len
|
|
|
|
Returns the length of the vector
|
|
*/
|
|
fn len<T>(t: t<T>) -> uint {
|
|
ret (*t).len;
|
|
}
|
|
|
|
/*
|
|
Function: ptr
|
|
|
|
Returns a pointer to the first element of the vector
|
|
*/
|
|
unsafe fn ptr<T>(t: t<T>) -> *mutable T {
|
|
ret (*t).base;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
import ctypes::*;
|
|
|
|
#[nolink]
|
|
#[abi = "cdecl"]
|
|
native mod libc {
|
|
fn malloc(n: size_t) -> *mutable u8;
|
|
fn free(m: *mutable u8);
|
|
}
|
|
|
|
fn malloc(n: size_t) -> t<u8> {
|
|
let mem = libc::malloc(n);
|
|
|
|
assert mem as int != 0;
|
|
|
|
ret unsafe { create_with_dtor(mem, n, bind libc::free(mem)) };
|
|
}
|
|
|
|
#[test]
|
|
fn test_basic() {
|
|
let cv = malloc(16u);
|
|
|
|
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(target_os = "win32"))]
|
|
fn test_overrun_get() {
|
|
let cv = malloc(16u);
|
|
|
|
get(cv, 17u);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
#[ignore(cfg(target_os = "win32"))]
|
|
fn test_overrun_set() {
|
|
let cv = malloc(16u);
|
|
|
|
set(cv, 17u, 0u8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_and_I_mean_it() {
|
|
let cv = malloc(16u);
|
|
let p = unsafe { ptr(cv) };
|
|
|
|
set(cv, 0u, 32u8);
|
|
set(cv, 1u, 33u8);
|
|
assert unsafe { *p } == 32u8;
|
|
set(cv, 2u, 34u8); /* safety */
|
|
}
|
|
|
|
} |