| # RawVec | |
| We've actually reached an interesting situation here: we've duplicated the logic | |
| for specifying a buffer and freeing its memory in Vec and IntoIter. Now that | |
| we've implemented it and identified *actual* logic duplication, this is a good | |
| time to perform some logic compression. | |
| We're going to abstract out the `(ptr, cap)` pair and give them the logic for | |
| allocating, growing, and freeing: | |
| <!-- ignore: simplified code --> | |
| ```rust,ignore | |
| struct RawVec<T> { | |
| ptr: NonNull<T>, | |
| cap: usize, | |
| } | |
| unsafe impl<T: Send> Send for RawVec<T> {} | |
| unsafe impl<T: Sync> Sync for RawVec<T> {} | |
| impl<T> RawVec<T> { | |
| fn new() -> Self { | |
| assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support"); | |
| RawVec { | |
| ptr: NonNull::dangling(), | |
| cap: 0, | |
| } | |
| } | |
| fn grow(&mut self) { | |
| // This can't overflow because we ensure self.cap <= isize::MAX. | |
| let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap }; | |
| // Layout::array checks that the number of bytes is <= usize::MAX, | |
| // but this is redundant since old_layout.size() <= isize::MAX, | |
| // so the `unwrap` should never fail. | |
| let new_layout = Layout::array::<T>(new_cap).unwrap(); | |
| // Ensure that the new allocation doesn't exceed `isize::MAX` bytes. | |
| assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large"); | |
| let new_ptr = if self.cap == 0 { | |
| unsafe { alloc::alloc(new_layout) } | |
| } else { | |
| let old_layout = Layout::array::<T>(self.cap).unwrap(); | |
| let old_ptr = self.ptr.as_ptr() as *mut u8; | |
| unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } | |
| }; | |
| // If allocation fails, `new_ptr` will be null, in which case we abort. | |
| self.ptr = match NonNull::new(new_ptr as *mut T) { | |
| Some(p) => p, | |
| None => alloc::handle_alloc_error(new_layout), | |
| }; | |
| self.cap = new_cap; | |
| } | |
| } | |
| impl<T> Drop for RawVec<T> { | |
| fn drop(&mut self) { | |
| if self.cap != 0 { | |
| let layout = Layout::array::<T>(self.cap).unwrap(); | |
| unsafe { | |
| alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); | |
| } | |
| } | |
| } | |
| } | |
| ``` | |
| And change Vec as follows: | |
| <!-- ignore: simplified code --> | |
| ```rust,ignore | |
| pub struct Vec<T> { | |
| buf: RawVec<T>, | |
| len: usize, | |
| } | |
| impl<T> Vec<T> { | |
| fn ptr(&self) -> *mut T { | |
| self.buf.ptr.as_ptr() | |
| } | |
| fn cap(&self) -> usize { | |
| self.buf.cap | |
| } | |
| pub fn new() -> Self { | |
| Vec { | |
| buf: RawVec::new(), | |
| len: 0, | |
| } | |
| } | |
| // push/pop/insert/remove largely unchanged: | |
| // * `self.ptr.as_ptr() -> self.ptr()` | |
| // * `self.cap -> self.cap()` | |
| // * `self.grow() -> self.buf.grow()` | |
| } | |
| impl<T> Drop for Vec<T> { | |
| fn drop(&mut self) { | |
| while let Some(_) = self.pop() {} | |
| // deallocation is handled by RawVec | |
| } | |
| } | |
| ``` | |
| And finally we can really simplify IntoIter: | |
| <!-- ignore: simplified code --> | |
| ```rust,ignore | |
| pub struct IntoIter<T> { | |
| _buf: RawVec<T>, // we don't actually care about this. Just need it to live. | |
| start: *const T, | |
| end: *const T, | |
| } | |
| // next and next_back literally unchanged since they never referred to the buf | |
| impl<T> Drop for IntoIter<T> { | |
| fn drop(&mut self) { | |
| // only need to ensure all our elements are read; | |
| // buffer will clean itself up afterwards. | |
| for _ in &mut *self {} | |
| } | |
| } | |
| impl<T> IntoIterator for Vec<T> { | |
| type Item = T; | |
| type IntoIter = IntoIter<T>; | |
| fn into_iter(self) -> IntoIter<T> { | |
| // need to use ptr::read to unsafely move the buf out since it's | |
| // not Copy, and Vec implements Drop (so we can't destructure it). | |
| let buf = unsafe { ptr::read(&self.buf) }; | |
| let len = self.len; | |
| mem::forget(self); | |
| IntoIter { | |
| start: buf.ptr.as_ptr(), | |
| end: if buf.cap == 0 { | |
| // can't offset off of a pointer unless it's part of an allocation | |
| buf.ptr.as_ptr() | |
| } else { | |
| unsafe { buf.ptr.as_ptr().add(len) } | |
| }, | |
| _buf: buf, | |
| } | |
| } | |
| } | |
| ``` | |
| Much better. |