diff --git a/src/lib/c_vec.rs b/src/lib/c_vec.rs new file mode 100644 index 0000000000000..ff5f636d103c3 --- /dev/null +++ b/src/lib/c_vec.rs @@ -0,0 +1,99 @@ +/* +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 size; +export ptr; + +/* + Type: t + + The type representing a native chunk of memory. Wrapped in a tag for + opacity; FIXME #818 when it is possible to have truly opaque types, this + should be revisited. + */ + +tag t { + t({ base: *mutable T, size: uint, rsrc: @dtor_res}); +} + +resource dtor_res(dtor: option::t) { + alt dtor { + option::none. { } + option::some(f) { f(); } + } +} + +/* + Section: Introduction forms + */ + +unsafe fn create(base: *mutable T, size: uint) -> t { + ret t({base: base, + size: size, + rsrc: @dtor_res(option::none) + }); +} + +unsafe fn create_with_dtor(base: *mutable T, size: uint, dtor: fn@()) + -> t { + ret t({base: base, + size: size, + rsrc: @dtor_res(option::some(dtor)) + }); +} + +/* + Section: Operations + */ + +fn get(t: t, ofs: uint) -> T { + assert ofs < (*t).size; + ret unsafe { *ptr::mut_offset((*t).base, ofs) }; +} + +fn set(t: t, ofs: uint, v: T) { + assert ofs < (*t).size; + unsafe { *ptr::mut_offset((*t).base, ofs) = v }; +} + +/* + Section: Elimination forms + */ + +fn size(t: t) -> uint { + ret (*t).size; +} + +unsafe fn ptr(t: t) -> *mutable T { + ret (*t).base; +} diff --git a/src/lib/ptr.rs b/src/lib/ptr.rs index 1654f9188a08f..0372b17cdf1d2 100644 --- a/src/lib/ptr.rs +++ b/src/lib/ptr.rs @@ -34,6 +34,16 @@ fn offset(ptr: *T, count: uint) -> *T { ret rusti::ptr_offset(ptr, count); } +/* +Function: mut_offset + +Calculate the offset from a mutable pointer +*/ +fn mut_offset(ptr: *mutable T, count: uint) -> *mutable T { + ret rusti::ptr_offset(ptr as *T, count) as *mutable T; +} + + /* Function: null diff --git a/src/lib/std.rc b/src/lib/std.rc index 342e3fe394d17..555828f79c3fd 100644 --- a/src/lib/std.rc +++ b/src/lib/std.rc @@ -10,7 +10,7 @@ export box, char, float, int, str, ptr; export uint, u8, u32, u64, vec, bool; export comm, fs, io, net, run, sys, task; -export ctypes, either, option, result, four, tri, util; +export c_vec, ctypes, either, option, result, four, tri, util; export bitv, deque, fun_treemap, list, map, smallintmap, sort, treemap, ufind; export rope; export math, math_f32, math_f64; @@ -50,6 +50,7 @@ mod task; // Utility modules +mod c_vec; mod ctypes; mod cmath; /* unexported */ mod either; diff --git a/src/test/stdtest/c_vec.rs b/src/test/stdtest/c_vec.rs new file mode 100644 index 0000000000000..2af2398662cfd --- /dev/null +++ b/src/test/stdtest/c_vec.rs @@ -0,0 +1,57 @@ +// -*- rust -*- +use std; +import std::c_vec::*; +import std::ctypes::*; + +#[link_name = ""] +#[abi = "cdecl"] +native mod libc { + fn malloc(n: size_t) -> *mutable u8; + fn free(m: *mutable u8); +} + +fn malloc(n: size_t) -> t { + 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 size(cv) == 16u; +} + +#[test] +#[should_fail] +fn test_overrun_get() { + let cv = malloc(16u); + + get(cv, 17u); +} + +#[test] +#[should_fail] +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 */ +} diff --git a/src/test/stdtest/stdtest.rc b/src/test/stdtest/stdtest.rc index a31f2430629b3..9d714f8f9c530 100644 --- a/src/test/stdtest/stdtest.rc +++ b/src/test/stdtest/stdtest.rc @@ -3,6 +3,7 @@ use std; mod bitv; mod bool; mod box; +mod c_vec; mod char; mod comm; mod deque;