]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # RawVec |
c1a9b12d SL |
2 | |
3 | We've actually reached an interesting situation here: we've duplicated the logic | |
4 | for specifying a buffer and freeing its memory in Vec and IntoIter. Now that | |
5 | we've implemented it and identified *actual* logic duplication, this is a good | |
6 | time to perform some logic compression. | |
7 | ||
8 | We're going to abstract out the `(ptr, cap)` pair and give them the logic for | |
9 | allocating, growing, and freeing: | |
10 | ||
136023e0 | 11 | <!-- ignore: simplified code --> |
c1a9b12d SL |
12 | ```rust,ignore |
13 | struct RawVec<T> { | |
17df50a5 | 14 | ptr: NonNull<T>, |
c1a9b12d | 15 | cap: usize, |
17df50a5 | 16 | _marker: PhantomData<T>, |
c1a9b12d SL |
17 | } |
18 | ||
17df50a5 XL |
19 | unsafe impl<T: Send> Send for RawVec<T> {} |
20 | unsafe impl<T: Sync> Sync for RawVec<T> {} | |
21 | ||
c1a9b12d SL |
22 | impl<T> RawVec<T> { |
23 | fn new() -> Self { | |
17df50a5 XL |
24 | assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support"); |
25 | RawVec { | |
26 | ptr: NonNull::dangling(), | |
27 | cap: 0, | |
28 | _marker: PhantomData, | |
29 | } | |
c1a9b12d SL |
30 | } |
31 | ||
c1a9b12d | 32 | fn grow(&mut self) { |
17df50a5 XL |
33 | let (new_cap, new_layout) = if self.cap == 0 { |
34 | (1, Layout::array::<T>(1).unwrap()) | |
35 | } else { | |
36 | // This can't overflow because we ensure self.cap <= isize::MAX. | |
37 | let new_cap = 2 * self.cap; | |
38 | ||
39 | // Layout::array checks that the number of bytes is <= usize::MAX, | |
40 | // but this is redundant since old_layout.size() <= isize::MAX, | |
41 | // so the `unwrap` should never fail. | |
42 | let new_layout = Layout::array::<T>(new_cap).unwrap(); | |
43 | (new_cap, new_layout) | |
44 | }; | |
45 | ||
46 | // Ensure that the new allocation doesn't exceed `isize::MAX` bytes. | |
47 | assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large"); | |
48 | ||
49 | let new_ptr = if self.cap == 0 { | |
50 | unsafe { alloc::alloc(new_layout) } | |
51 | } else { | |
52 | let old_layout = Layout::array::<T>(self.cap).unwrap(); | |
53 | let old_ptr = self.ptr.as_ptr() as *mut u8; | |
54 | unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } | |
55 | }; | |
56 | ||
57 | // If allocation fails, `new_ptr` will be null, in which case we abort. | |
58 | self.ptr = match NonNull::new(new_ptr as *mut T) { | |
59 | Some(p) => p, | |
60 | None => alloc::handle_alloc_error(new_layout), | |
61 | }; | |
62 | self.cap = new_cap; | |
c1a9b12d SL |
63 | } |
64 | } | |
65 | ||
c1a9b12d SL |
66 | impl<T> Drop for RawVec<T> { |
67 | fn drop(&mut self) { | |
68 | if self.cap != 0 { | |
17df50a5 | 69 | let layout = Layout::array::<T>(self.cap).unwrap(); |
c1a9b12d | 70 | unsafe { |
17df50a5 | 71 | alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); |
c1a9b12d SL |
72 | } |
73 | } | |
74 | } | |
75 | } | |
76 | ``` | |
77 | ||
78 | And change Vec as follows: | |
79 | ||
136023e0 | 80 | <!-- ignore: simplified code --> |
c1a9b12d SL |
81 | ```rust,ignore |
82 | pub struct Vec<T> { | |
83 | buf: RawVec<T>, | |
84 | len: usize, | |
85 | } | |
86 | ||
87 | impl<T> Vec<T> { | |
17df50a5 XL |
88 | fn ptr(&self) -> *mut T { |
89 | self.buf.ptr.as_ptr() | |
90 | } | |
c1a9b12d | 91 | |
17df50a5 XL |
92 | fn cap(&self) -> usize { |
93 | self.buf.cap | |
94 | } | |
c1a9b12d SL |
95 | |
96 | pub fn new() -> Self { | |
17df50a5 XL |
97 | Vec { |
98 | buf: RawVec::new(), | |
99 | len: 0, | |
100 | } | |
c1a9b12d SL |
101 | } |
102 | ||
103 | // push/pop/insert/remove largely unchanged: | |
5869c6ff | 104 | // * `self.ptr.as_ptr() -> self.ptr()` |
c1a9b12d | 105 | // * `self.cap -> self.cap()` |
17df50a5 | 106 | // * `self.grow() -> self.buf.grow()` |
c1a9b12d SL |
107 | } |
108 | ||
109 | impl<T> Drop for Vec<T> { | |
110 | fn drop(&mut self) { | |
111 | while let Some(_) = self.pop() {} | |
112 | // deallocation is handled by RawVec | |
113 | } | |
114 | } | |
115 | ``` | |
116 | ||
117 | And finally we can really simplify IntoIter: | |
118 | ||
136023e0 | 119 | <!-- ignore: simplified code --> |
c1a9b12d | 120 | ```rust,ignore |
5869c6ff | 121 | pub struct IntoIter<T> { |
c1a9b12d SL |
122 | _buf: RawVec<T>, // we don't actually care about this. Just need it to live. |
123 | start: *const T, | |
124 | end: *const T, | |
125 | } | |
126 | ||
127 | // next and next_back literally unchanged since they never referred to the buf | |
128 | ||
129 | impl<T> Drop for IntoIter<T> { | |
130 | fn drop(&mut self) { | |
131 | // only need to ensure all our elements are read; | |
132 | // buffer will clean itself up afterwards. | |
133 | for _ in &mut *self {} | |
134 | } | |
135 | } | |
136 | ||
137 | impl<T> Vec<T> { | |
138 | pub fn into_iter(self) -> IntoIter<T> { | |
139 | unsafe { | |
140 | // need to use ptr::read to unsafely move the buf out since it's | |
141 | // not Copy, and Vec implements Drop (so we can't destructure it). | |
142 | let buf = ptr::read(&self.buf); | |
143 | let len = self.len; | |
144 | mem::forget(self); | |
145 | ||
146 | IntoIter { | |
5869c6ff | 147 | start: buf.ptr.as_ptr(), |
94222f64 XL |
148 | end: if buf.cap == 0 { |
149 | // can't offset off of a pointer unless it's part of an allocation | |
150 | buf.ptr.as_ptr() | |
151 | } else { | |
152 | buf.ptr.as_ptr().add(len) | |
153 | }, | |
c1a9b12d SL |
154 | _buf: buf, |
155 | } | |
156 | } | |
157 | } | |
158 | } | |
159 | ``` | |
160 | ||
161 | Much better. |