@@ -11,19 +11,20 @@ allocation.
11
11
IntoIter needs to be DoubleEnded as well, to enable reading from both ends.
12
12
Reading from the back could just be implemented as calling ` pop ` , but reading
13
13
from the front is harder. We could call ` remove(0) ` but that would be insanely
14
- expensive. Instead we're going to just use ptr::read to copy values out of either
15
- end of the Vec without mutating the buffer at all.
14
+ expensive. Instead we're going to just use ptr::read to copy values out of
15
+ either end of the Vec without mutating the buffer at all.
16
16
17
17
To do this we're going to use a very common C idiom for array iteration. We'll
18
- make two pointers; one that points to the start of the array, and one that points
19
- to one-element past the end. When we want an element from one end, we'll read out
20
- the value pointed to at that end and move the pointer over by one. When the two
21
- pointers are equal, we know we're done.
18
+ make two pointers; one that points to the start of the array, and one that
19
+ points to one-element past the end. When we want an element from one end, we'll
20
+ read out the value pointed to at that end and move the pointer over by one. When
21
+ the two pointers are equal, we know we're done.
22
22
23
23
Note that the order of read and offset are reversed for ` next ` and ` next_back `
24
24
For ` next_back ` the pointer is always * after* the element it wants to read next,
25
25
while for ` next ` the pointer is always * at* the element it wants to read next.
26
- To see why this is, consider the case where every element but one has been yielded.
26
+ To see why this is, consider the case where every element but one has been
27
+ yielded.
27
28
28
29
The array looks like this:
29
30
@@ -35,6 +36,10 @@ The array looks like this:
35
36
If E pointed directly at the element it wanted to yield next, it would be
36
37
indistinguishable from the case where there are no more elements to yield.
37
38
39
+ Although we don't actually care about it during iteration, we also need to hold
40
+ onto the Vec's allocation information in order to free it once IntoIter is
41
+ dropped.
42
+
38
43
So we're going to use the following struct:
39
44
40
45
``` rust,ignore
@@ -46,8 +51,8 @@ struct IntoIter<T> {
46
51
}
47
52
```
48
53
49
- One last subtle detail: if our Vec is empty, we want to produce an empty iterator.
50
- This will actually technically fall out doing the naive thing of:
54
+ One last subtle detail: if our Vec is empty, we want to produce an empty
55
+ iterator. This will actually technically fall out doing the naive thing of:
51
56
52
57
``` text
53
58
start = ptr
@@ -155,139 +160,3 @@ impl<T> Drop for IntoIter<T> {
155
160
}
156
161
}
157
162
```
158
-
159
- We've actually reached an interesting situation here: we've duplicated the logic
160
- for specifying a buffer and freeing its memory. Now that we've implemented it and
161
- identified * actual* logic duplication, this is a good time to perform some logic
162
- compression.
163
-
164
- We're going to abstract out the ` (ptr, cap) ` pair and give them the logic for
165
- allocating, growing, and freeing:
166
-
167
- ``` rust,ignore
168
-
169
- struct RawVec<T> {
170
- ptr: Unique<T>,
171
- cap: usize,
172
- }
173
-
174
- impl<T> RawVec<T> {
175
- fn new() -> Self {
176
- assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support");
177
- unsafe {
178
- RawVec { ptr: Unique::new(heap::EMPTY as *mut T), cap: 0 }
179
- }
180
- }
181
-
182
- // unchanged from Vec
183
- fn grow(&mut self) {
184
- unsafe {
185
- let align = mem::align_of::<T>();
186
- let elem_size = mem::size_of::<T>();
187
-
188
- let (new_cap, ptr) = if self.cap == 0 {
189
- let ptr = heap::allocate(elem_size, align);
190
- (1, ptr)
191
- } else {
192
- let new_cap = 2 * self.cap;
193
- let ptr = heap::reallocate(*self.ptr as *mut _,
194
- self.cap * elem_size,
195
- new_cap * elem_size,
196
- align);
197
- (new_cap, ptr)
198
- };
199
-
200
- // If allocate or reallocate fail, we'll get `null` back
201
- if ptr.is_null() { oom() }
202
-
203
- self.ptr = Unique::new(ptr as *mut _);
204
- self.cap = new_cap;
205
- }
206
- }
207
- }
208
-
209
-
210
- impl<T> Drop for RawVec<T> {
211
- fn drop(&mut self) {
212
- if self.cap != 0 {
213
- let align = mem::align_of::<T>();
214
- let elem_size = mem::size_of::<T>();
215
- let num_bytes = elem_size * self.cap;
216
- unsafe {
217
- heap::deallocate(*self.ptr as *mut _, num_bytes, align);
218
- }
219
- }
220
- }
221
- }
222
- ```
223
-
224
- And change vec as follows:
225
-
226
- ``` rust,ignore
227
- pub struct Vec<T> {
228
- buf: RawVec<T>,
229
- len: usize,
230
- }
231
-
232
- impl<T> Vec<T> {
233
- fn ptr(&self) -> *mut T { *self.buf.ptr }
234
-
235
- fn cap(&self) -> usize { self.buf.cap }
236
-
237
- pub fn new() -> Self {
238
- Vec { buf: RawVec::new(), len: 0 }
239
- }
240
-
241
- // push/pop/insert/remove largely unchanged:
242
- // * `self.ptr -> self.ptr()`
243
- // * `self.cap -> self.cap()`
244
- // * `self.grow -> self.buf.grow()`
245
- }
246
-
247
- impl<T> Drop for Vec<T> {
248
- fn drop(&mut self) {
249
- while let Some(_) = self.pop() {}
250
- // deallocation is handled by RawVec
251
- }
252
- }
253
- ```
254
-
255
- And finally we can really simplify IntoIter:
256
-
257
- ``` rust,ignore
258
- struct IntoIter<T> {
259
- _buf: RawVec<T>, // we don't actually care about this. Just need it to live.
260
- start: *const T,
261
- end: *const T,
262
- }
263
-
264
- // next and next_back literally unchanged since they never referred to the buf
265
-
266
- impl<T> Drop for IntoIter<T> {
267
- fn drop(&mut self) {
268
- // only need to ensure all our elements are read;
269
- // buffer will clean itself up afterwards.
270
- for _ in &mut *self {}
271
- }
272
- }
273
-
274
- impl<T> Vec<T> {
275
- pub fn into_iter(self) -> IntoIter<T> {
276
- unsafe {
277
- // need to use ptr::read to unsafely move the buf out since it's
278
- // not Copy.
279
- let buf = ptr::read(&self.buf);
280
- let len = self.len;
281
- mem::forget(self);
282
-
283
- IntoIter {
284
- start: *buf.ptr,
285
- end: buf.ptr.offset(len as isize),
286
- _buf: buf,
287
- }
288
- }
289
- }
290
- }
291
- ```
292
-
293
- Much better.
0 commit comments