Skip to content

Commit 870fb7d

Browse files
committed
c_vec: Modernize
Generally use more modern constructs (such as using `CVec::new()` as constructor and move to more method usage). Potentially controversial changes: * change `get()` to return a reference instead of cloning * remove `set()`, add `get_mut()` instead * add an `unwrap()` method that destroys the CVec without running any associated destructor
1 parent 61443dc commit 870fb7d

File tree

1 file changed

+122
-104
lines changed

1 file changed

+122
-104
lines changed

src/libextra/c_vec.rs

Lines changed: 122 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,26 @@
2424
* closure is provided. Safety is ensured by bounds-checking accesses, which
2525
* are marshalled through get and set functions.
2626
*
27-
* There are three unsafe functions: the two introduction forms, and the
28-
* pointer elimination form. The introduction forms are unsafe for the
27+
* There are three unsafe functions: the two constructors, and the
28+
* unwrap method. The constructors are unsafe for the
2929
* obvious reason (they act on a pointer that cannot be checked inside the
30-
* method), but the elimination form is somewhat more subtle in its unsafety.
31-
* By using a pointer taken from a c_vec::t without keeping a reference to the
32-
* c_vec::t itself around, the CVec could be garbage collected, and the
33-
* memory within could be destroyed. There are legitimate uses for the
34-
* pointer elimination form -- for instance, to pass memory back into C -- but
35-
* great care must be taken to ensure that a reference to the c_vec::t is
36-
* still held if needed.
30+
* method), but `unwrap()` is somewhat more subtle in its unsafety.
31+
* It returns the contained pointer, but at the same time destroys the CVec
32+
* without running its destructor. This can be used to pass memory back to
33+
* C, but care must be taken that the ownership of underlying resources are
34+
* handled correctly, i.e. that allocated memory is eventually freed
35+
* if necessary.
3736
*/
3837

3938
use std::ptr;
40-
use std::util;
4139

4240
/**
4341
* The type representing a foreign chunk of memory
4442
*/
4543
pub struct CVec<T> {
4644
priv base: *mut T,
4745
priv len: uint,
48-
priv rsrc: @DtorRes,
46+
priv rsrc: DtorRes,
4947
}
5048

5149
struct DtorRes {
@@ -55,7 +53,7 @@ struct DtorRes {
5553
#[unsafe_destructor]
5654
impl Drop for DtorRes {
5755
fn drop(&mut self) {
58-
let dtor = util::replace(&mut self.dtor, None);
56+
let dtor = self.dtor.take();
5957
match dtor {
6058
None => (),
6159
Some(f) => f()
@@ -71,138 +69,158 @@ impl DtorRes {
7169
}
7270
}
7371

74-
/*
75-
Section: Introduction forms
76-
*/
72+
impl <T> CVec<T> {
73+
/**
74+
* Create a `CVec` from a raw pointer to a buffer with a given length.
75+
*
76+
* Fails if the given pointer is null.
77+
*
78+
* # Arguments
79+
*
80+
* * base - A raw pointer to a buffer
81+
* * len - The number of elements in the buffer
82+
*/
83+
pub unsafe fn new(base: *mut T, len: uint) -> CVec<T> {
84+
assert!(base != ptr::mut_null());
85+
CVec {
86+
base: base,
87+
len: len,
88+
rsrc: DtorRes::new(None)
89+
}
90+
}
7791

78-
/**
79-
* Create a `CVec` from a foreign buffer with a given length.
80-
*
81-
* # Arguments
82-
*
83-
* * base - A foreign pointer to a buffer
84-
* * len - The number of elements in the buffer
85-
*/
86-
pub unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
87-
return CVec {
88-
base: base,
89-
len: len,
90-
rsrc: @DtorRes::new(None)
91-
};
92-
}
92+
/**
93+
* Create a `CVec` from a foreign buffer, with a given length,
94+
* and a function to run upon destruction.
95+
*
96+
* Fails if the given pointer is null.
97+
*
98+
* # Arguments
99+
*
100+
* * base - A foreign pointer to a buffer
101+
* * len - The number of elements in the buffer
102+
* * dtor - A proc to run when the value is destructed, useful
103+
* for freeing the buffer, etc.
104+
*/
105+
pub unsafe fn new_with_dtor(base: *mut T, len: uint, dtor: proc()) -> CVec<T> {
106+
assert!(base != ptr::mut_null());
107+
CVec {
108+
base: base,
109+
len: len,
110+
rsrc: DtorRes::new(Some(dtor))
111+
}
112+
}
93113

94-
/**
95-
* Create a `CVec` from a foreign buffer, with a given length,
96-
* and a function to run upon destruction.
97-
*
98-
* # Arguments
99-
*
100-
* * base - A foreign pointer to a buffer
101-
* * len - The number of elements in the buffer
102-
* * dtor - A function to run when the value is destructed, useful
103-
* for freeing the buffer, etc.
104-
*/
105-
pub unsafe fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: proc())
106-
-> CVec<T> {
107-
return CVec{
108-
base: base,
109-
len: len,
110-
rsrc: @DtorRes::new(Some(dtor))
111-
};
112-
}
114+
/**
115+
* Retrieves an element at a given index
116+
*
117+
* Fails if `ofs` is greater or equal to the length of the vector
118+
*/
119+
pub fn get<'a>(&'a self, ofs: uint) -> &'a T {
120+
assert!(ofs < self.len);
121+
unsafe {
122+
&*ptr::mut_offset(self.base, ofs as int)
123+
}
124+
}
113125

114-
/*
115-
Section: Operations
116-
*/
126+
/**
127+
* Retrieves a mutable element at a given index
128+
*
129+
* Fails if `ofs` is greater or equal to the length of the vector
130+
*/
131+
pub fn get_mut<'a>(&'a mut self, ofs: uint) -> &'a mut T {
132+
assert!(ofs < self.len);
133+
unsafe {
134+
&mut *ptr::mut_offset(self.base, ofs as int)
135+
}
136+
}
117137

118-
/**
119-
* Retrieves an element at a given index
120-
*
121-
* Fails if `ofs` is greater or equal to the length of the vector
122-
*/
123-
pub fn get<T:Clone>(t: CVec<T>, ofs: uint) -> T {
124-
assert!(ofs < len(t));
125-
return unsafe {
126-
(*ptr::mut_offset(t.base, ofs as int)).clone()
127-
};
138+
/**
139+
* Unwrap the pointer without running the destructor
140+
*
141+
* This method retrieves the underlying pointer, and in the process
142+
* destroys the CVec but without running the destructor. A use case
143+
* would be transferring ownership of the buffer to a C function, as
144+
* in this case you would not want to run the destructor.
145+
*
146+
* Note that if you want to access the underlying pointer without
147+
* cancelling the destructor, you can simply call `transmute` on the return
148+
* value of `get(0)`.
149+
*/
150+
pub unsafe fn unwrap(mut self) -> *mut T {
151+
self.rsrc.dtor = None;
152+
self.base
153+
}
128154
}
129155

130-
/**
131-
* Sets the value of an element at a given index
132-
*
133-
* Fails if `ofs` is greater or equal to the length of the vector
134-
*/
135-
pub fn set<T>(t: CVec<T>, ofs: uint, v: T) {
136-
assert!(ofs < len(t));
137-
unsafe { *ptr::mut_offset(t.base, ofs as int) = v };
156+
impl <T> Container for CVec<T> {
157+
/// Returns the length of the vector
158+
fn len(&self) -> uint { self.len }
138159
}
139160

140-
/*
141-
Section: Elimination forms
142-
*/
143-
144-
/// Returns the length of the vector
145-
pub fn len<T>(t: CVec<T>) -> uint { t.len }
146-
147-
/// Returns a pointer to the first element of the vector
148-
pub unsafe fn ptr<T>(t: CVec<T>) -> *mut T { t.base }
149-
150161
#[cfg(test)]
151162
mod tests {
152163

153-
use c_vec::*;
164+
use super::*;
154165

155166
use std::libc::*;
156167
use std::libc;
168+
use std::ptr;
157169

158-
fn malloc(n: size_t) -> CVec<u8> {
170+
fn malloc(n: uint) -> CVec<u8> {
159171
unsafe {
160-
let mem = libc::malloc(n);
172+
let mem = libc::malloc(n as size_t);
161173

162174
assert!(mem as int != 0);
163175

164-
return c_vec_with_dtor(mem as *mut u8,
165-
n as uint,
166-
proc() unsafe { libc::free(mem); });
176+
CVec::new_with_dtor(mem as *mut u8, n,
177+
proc() { libc::free(mem); })
167178
}
168179
}
169180

170181
#[test]
171182
fn test_basic() {
172-
let cv = malloc(16u as size_t);
183+
let mut cv = malloc(16);
173184

174-
set(cv, 3u, 8u8);
175-
set(cv, 4u, 9u8);
176-
assert_eq!(get(cv, 3u), 8u8);
177-
assert_eq!(get(cv, 4u), 9u8);
178-
assert_eq!(len(cv), 16u);
185+
*cv.get_mut(3) = 8;
186+
*cv.get_mut(4) = 9;
187+
assert_eq!(*cv.get(3), 8);
188+
assert_eq!(*cv.get(4), 9);
189+
assert_eq!(cv.len(), 16);
190+
}
191+
192+
#[test]
193+
#[should_fail]
194+
fn test_fail_at_null() {
195+
unsafe {
196+
CVec::new(ptr::mut_null::<u8>(), 9);
197+
}
179198
}
180199

181200
#[test]
182201
#[should_fail]
183202
fn test_overrun_get() {
184-
let cv = malloc(16u as size_t);
203+
let cv = malloc(16);
185204

186-
get(cv, 17u);
205+
cv.get(17);
187206
}
188207

189208
#[test]
190209
#[should_fail]
191210
fn test_overrun_set() {
192-
let cv = malloc(16u as size_t);
211+
let mut cv = malloc(16);
193212

194-
set(cv, 17u, 0u8);
213+
*cv.get_mut(17) = 0;
195214
}
196215

197216
#[test]
198-
fn test_and_I_mean_it() {
199-
let cv = malloc(16u as size_t);
200-
let p = unsafe { ptr(cv) };
201-
202-
set(cv, 0u, 32u8);
203-
set(cv, 1u, 33u8);
204-
assert_eq!(unsafe { *p }, 32u8);
205-
set(cv, 2u, 34u8); /* safety */
217+
fn test_unwrap() {
218+
unsafe {
219+
let cv = CVec::new_with_dtor(1 as *mut int, 0,
220+
proc() { fail!("Don't run this destructor!") });
221+
let p = cv.unwrap();
222+
assert_eq!(p, 1 as *mut int);
223+
}
206224
}
207225

208226
}

0 commit comments

Comments
 (0)