]> git.lizzy.rs Git - rust.git/blob - src/libextra/c_vec.rs
e656360aa5a793c859d2059d284ad43131e0b681
[rust.git] / src / libextra / c_vec.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12  * Library to interface with chunks of memory allocated in C.
13  *
14  * It is often desirable to safely interface with memory allocated from C,
15  * encapsulating the unsafety into allocation and destruction time.  Indeed,
16  * allocating memory externally is currently the only way to give Rust shared
17  * mut state with C programs that keep their own references; vectors are
18  * unsuitable because they could be reallocated or moved at any time, and
19  * importing C memory into a vector takes a one-time snapshot of the memory.
20  *
21  * This module simplifies the usage of such external blocks of memory.  Memory
22  * is encapsulated into an opaque object after creation; the lifecycle of the
23  * memory can be optionally managed by Rust, if an appropriate destructor
24  * closure is provided.  Safety is ensured by bounds-checking accesses, which
25  * are marshalled through get and set functions.
26  *
27  * There are three unsafe functions: the two introduction forms, and the
28  * pointer elimination form.  The introduction forms are unsafe for the
29  * 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.
37  */
38
39
40 use std::option;
41 use std::ptr;
42
43 /**
44  * The type representing a foreign chunk of memory
45  *
46  */
47 pub struct CVec<T> {
48     priv base: *mut T,
49     priv len: uint,
50     priv rsrc: @DtorRes
51 }
52
53 struct DtorRes {
54   dtor: Option<@fn()>,
55 }
56
57 #[unsafe_destructor]
58 impl Drop for DtorRes {
59     fn drop(&self) {
60         match self.dtor {
61             option::None => (),
62             option::Some(f) => f()
63         }
64     }
65 }
66
67 fn DtorRes(dtor: Option<@fn()>) -> DtorRes {
68     DtorRes {
69         dtor: dtor
70     }
71 }
72
73 /*
74  Section: Introduction forms
75  */
76
77 /**
78  * Create a `CVec` from a foreign buffer with a given length.
79  *
80  * # Arguments
81  *
82  * * base - A foreign pointer to a buffer
83  * * len - The number of elements in the buffer
84  */
85 pub unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
86     return CVec{
87         base: base,
88         len: len,
89         rsrc: @DtorRes(option::None)
90     };
91 }
92
93 /**
94  * Create a `CVec` from a foreign buffer, with a given length,
95  * and a function to run upon destruction.
96  *
97  * # Arguments
98  *
99  * * base - A foreign pointer to a buffer
100  * * len - The number of elements in the buffer
101  * * dtor - A function to run when the value is destructed, useful
102  *          for freeing the buffer, etc.
103  */
104 pub unsafe fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: @fn())
105   -> CVec<T> {
106     return CVec{
107         base: base,
108         len: len,
109         rsrc: @DtorRes(option::Some(dtor))
110     };
111 }
112
113 /*
114  Section: Operations
115  */
116
117 /**
118  * Retrieves an element at a given index
119  *
120  * Fails if `ofs` is greater or equal to the length of the vector
121  */
122 pub fn get<T:Clone>(t: CVec<T>, ofs: uint) -> T {
123     assert!(ofs < len(t));
124     return unsafe {
125         (*ptr::mut_offset(t.base, ofs as int)).clone()
126     };
127 }
128
129 /**
130  * Sets the value of an element at a given index
131  *
132  * Fails if `ofs` is greater or equal to the length of the vector
133  */
134 pub fn set<T>(t: CVec<T>, ofs: uint, v: T) {
135     assert!(ofs < len(t));
136     unsafe { *ptr::mut_offset(t.base, ofs as int) = v };
137 }
138
139 /*
140  Section: Elimination forms
141  */
142
143 /// Returns the length of the vector
144 pub fn len<T>(t: CVec<T>) -> uint { t.len }
145
146 /// Returns a pointer to the first element of the vector
147 pub unsafe fn ptr<T>(t: CVec<T>) -> *mut T { t.base }
148
149 #[cfg(test)]
150 mod tests {
151
152     use c_vec::*;
153
154     use std::libc::*;
155     use std::libc;
156
157     fn malloc(n: size_t) -> CVec<u8> {
158         unsafe {
159             let mem = libc::malloc(n);
160
161             assert!(mem as int != 0);
162
163             c_vec_with_dtor(mem as *mut u8, n as uint, || free(mem))
164         }
165     }
166
167     #[test]
168     fn test_basic() {
169         let cv = malloc(16u as size_t);
170
171         set(cv, 3u, 8u8);
172         set(cv, 4u, 9u8);
173         assert_eq!(get(cv, 3u), 8u8);
174         assert_eq!(get(cv, 4u), 9u8);
175         assert_eq!(len(cv), 16u);
176     }
177
178     #[test]
179     #[should_fail]
180     #[ignore(cfg(windows))]
181     fn test_overrun_get() {
182         let cv = malloc(16u as size_t);
183
184         get(cv, 17u);
185     }
186
187     #[test]
188     #[should_fail]
189     #[ignore(cfg(windows))]
190     fn test_overrun_set() {
191         let cv = malloc(16u as size_t);
192
193         set(cv, 17u, 0u8);
194     }
195
196     #[test]
197     fn test_and_I_mean_it() {
198         let cv = malloc(16u as size_t);
199         let p = unsafe { ptr(cv) };
200
201         set(cv, 0u, 32u8);
202         set(cv, 1u, 33u8);
203         assert_eq!(unsafe { *p }, 32u8);
204         set(cv, 2u, 34u8); /* safety */
205     }
206
207 }