1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! 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 constructors, and the //! unwrap method. The constructors are unsafe for the //! obvious reason (they act on a pointer that cannot be checked inside the //! method), but `unwrap()` is somewhat more subtle in its unsafety. //! It returns the contained pointer, but at the same time destroys the CVec //! without running its destructor. This can be used to pass memory back to //! C, but care must be taken that the ownership of underlying resources are //! handled correctly, i.e. that allocated memory is eventually freed //! if necessary. #![experimental] use kinds::Send; use mem; use ops::{Drop, FnOnce}; use option::Option; use option::Option::{Some, None}; use ptr::PtrExt; use ptr; use raw; use slice::AsSlice; use thunk::{Thunk}; /// The type representing a foreign chunk of memory pub struct CVec<T> { base: *mut T, len: uint, dtor: Option<Thunk>, } #[unsafe_destructor] impl<T> Drop for CVec<T> { fn drop(&mut self) { match self.dtor.take() { None => (), Some(f) => f.invoke(()) } } } impl<T> CVec<T> { /// Create a `CVec` from a raw pointer to a buffer with a given length. /// /// Panics if the given pointer is null. The returned vector will not attempt /// to deallocate the vector when dropped. /// /// # Arguments /// /// * base - A raw pointer to a buffer /// * len - The number of elements in the buffer pub unsafe fn new(base: *mut T, len: uint) -> CVec<T> { assert!(base != ptr::null_mut()); CVec { base: base, len: len, dtor: None, } } /// Create a `CVec` from a foreign buffer, with a given length, /// and a function to run upon destruction. /// /// Panics if the given pointer is null. /// /// # Arguments /// /// * base - A foreign pointer to a buffer /// * len - The number of elements in the buffer /// * dtor - A fn to run when the value is destructed, useful /// for freeing the buffer, etc. pub unsafe fn new_with_dtor<F>(base: *mut T, len: uint, dtor: F) -> CVec<T> where F : FnOnce(), F : Send { assert!(base != ptr::null_mut()); let dtor: Thunk = Thunk::new(dtor); CVec { base: base, len: len, dtor: Some(dtor) } } /// View the stored data as a mutable slice. pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] { unsafe { mem::transmute(raw::Slice { data: self.base as *const T, len: self.len }) } } /// Retrieves an element at a given index, returning `None` if the requested /// index is greater than the length of the vector. pub fn get<'a>(&'a self, ofs: uint) -> Option<&'a T> { if ofs < self.len { Some(unsafe { &*self.base.offset(ofs as int) }) } else { None } } /// Retrieves a mutable element at a given index, returning `None` if the /// requested index is greater than the length of the vector. pub fn get_mut<'a>(&'a mut self, ofs: uint) -> Option<&'a mut T> { if ofs < self.len { Some(unsafe { &mut *self.base.offset(ofs as int) }) } else { None } } /// Unwrap the pointer without running the destructor /// /// This method retrieves the underlying pointer, and in the process /// destroys the CVec but without running the destructor. A use case /// would be transferring ownership of the buffer to a C function, as /// in this case you would not want to run the destructor. /// /// Note that if you want to access the underlying pointer without /// cancelling the destructor, you can simply call `transmute` on the return /// value of `get(0)`. pub unsafe fn into_inner(mut self) -> *mut T { self.dtor = None; self.base } /// Deprecated, use into_inner() instead #[deprecated = "renamed to into_inner()"] pub unsafe fn unwrap(self) -> *mut T { self.into_inner() } /// Returns the number of items in this vector. pub fn len(&self) -> uint { self.len } /// Returns whether this vector is empty. pub fn is_empty(&self) -> bool { self.len() == 0 } } impl<T> AsSlice<T> for CVec<T> { /// View the stored data as a slice. fn as_slice<'a>(&'a self) -> &'a [T] { unsafe { mem::transmute(raw::Slice { data: self.base as *const T, len: self.len }) } } } #[cfg(test)] mod tests { use prelude::*; use super::CVec; use libc; use ptr; fn malloc(n: uint) -> CVec<u8> { unsafe { let mem = ptr::Unique(libc::malloc(n as libc::size_t)); if mem.0.is_null() { ::alloc::oom() } CVec::new_with_dtor(mem.0 as *mut u8, n, move|| { libc::free(mem.0 as *mut libc::c_void); }) } } #[test] fn test_basic() { let mut cv = malloc(16); *cv.get_mut(3).unwrap() = 8; *cv.get_mut(4).unwrap() = 9; assert_eq!(*cv.get(3).unwrap(), 8); assert_eq!(*cv.get(4).unwrap(), 9); assert_eq!(cv.len(), 16); } #[test] #[should_fail] fn test_panic_at_null() { unsafe { CVec::new(ptr::null_mut::<u8>(), 9); } } #[test] fn test_overrun_get() { let cv = malloc(16); assert!(cv.get(17).is_none()); } #[test] fn test_overrun_set() { let mut cv = malloc(16); assert!(cv.get_mut(17).is_none()); } #[test] fn test_unwrap() { unsafe { let cv = CVec::new_with_dtor(1 as *mut int, 0, move|:| panic!("Don't run this destructor!")); let p = cv.unwrap(); assert_eq!(p, 1 as *mut int); } } }