From eed46286135f6bbaae4c08b38625e497c5816219 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Fri, 19 Aug 2022 09:57:10 -0700 Subject: [PATCH] 14.5.2 --- rust/src/chunk.rs | 111 +++++-------------------------------------- rust/src/main.rs | 1 + rust/src/vec.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 98 deletions(-) create mode 100644 rust/src/vec.rs diff --git a/rust/src/chunk.rs b/rust/src/chunk.rs index d94a15a..44d1e9f 100644 --- a/rust/src/chunk.rs +++ b/rust/src/chunk.rs @@ -1,52 +1,38 @@ -use std::alloc::{self, Layout}; -use std::marker::PhantomData; -use std::ops::Deref; -use std::ptr::{self, NonNull}; - #[repr(u8)] pub enum OpCode { Return, } +type Value = f32; + pub struct Chunk { - count: usize, - capacity: usize, - code: NonNull, - _marker: PhantomData, + code: Vec, + constants: Vec, } -unsafe impl Send for Chunk {} -unsafe impl Sync for Chunk {} - impl Chunk { pub fn new() -> Self { Chunk { - count: 0, - capacity: 0, - code: NonNull::dangling(), - _marker: PhantomData, + code: Vec::new(), + constants: Vec::new(), } } // https://doc.rust-lang.org/nomicon/vec/vec-push-pop.html pub fn write(&mut self, op_code: OpCode) { - if self.count == self.capacity { - self.grow(); - } - - unsafe { - ptr::write(self.code.as_ptr().add(self.count), op_code); - } + self.code.push(op_code); + } - // Can't fail, we'll OOM first. - self.count += 1; + pub fn add_constant(&mut self, value: Value) -> usize{ + self.constants.push(value); + self.constants.len() - 1 } pub fn disassemble(&self, name: &str) { println!("== {} ==", name); let mut offset = 0; - while offset < self.count { + while offset < self.code.len() { offset = self.disassemble_instruction(offset); } } @@ -54,7 +40,7 @@ impl Chunk { fn disassemble_instruction(&self, offset: usize) -> usize { print!("{:04} ", offset); - match self[offset] { + match self.code[offset] { OpCode::Return => self.simple_instruction("OP_RETURN", offset), } } @@ -63,57 +49,6 @@ impl Chunk { println!("{}", name); offset + 1 } - - // https://doc.rust-lang.org/nomicon/vec/vec-alloc.html - fn grow(&mut self) { - // Crafting Interpreters: GROW_CAPACITY - let new_cap = if self.capacity < 8 { - 8 - } else { - // This can't overflow since self.capacity <= isize::MAX. - self.capacity * 2 - }; - - // `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::(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" - ); - - // Crafting Interpreters: GROW_ARRAY / reallocate - // TODO: Handle shrinking maybe? We may need to - // extract these out if it's used elsewhere - // in the book - let new_ptr = if self.capacity == 0 { - unsafe { alloc::alloc(new_layout) } - } else { - let old_layout = Layout::array::(self.capacity).unwrap(); - let old_ptr = self.code.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.code = match NonNull::new(new_ptr as *mut OpCode) { - Some(p) => p, - None => alloc::handle_alloc_error(new_layout), - }; - self.capacity = new_cap; - } - - // https://doc.rust-lang.org/nomicon/vec/vec-push-pop.html - pub fn pop(&mut self) -> Option { - if self.count == 0 { - None - } else { - self.count -= 1; - unsafe { Some(ptr::read(self.code.as_ptr().add(self.count))) } - } - } } impl Default for Chunk { @@ -121,23 +56,3 @@ impl Default for Chunk { Self::new() } } - -// https://doc.rust-lang.org/nomicon/vec/vec-dealloc.html -impl Drop for Chunk { - fn drop(&mut self) { - if self.capacity != 0 { - while self.pop().is_some() {} - let layout = Layout::array::(self.capacity).unwrap(); - unsafe { - alloc::dealloc(self.code.as_ptr() as *mut u8, layout); - } - } - } -} - -impl Deref for Chunk { - type Target = [OpCode]; - fn deref(&self) -> &[OpCode] { - unsafe { std::slice::from_raw_parts(self.code.as_ptr(), self.count) } - } -} diff --git a/rust/src/main.rs b/rust/src/main.rs index 0e1a343..f1a76d2 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,6 +1,7 @@ use chunk::{Chunk, OpCode}; pub mod chunk; +pub mod vec; fn main() { let mut chunk = Chunk::default(); diff --git a/rust/src/vec.rs b/rust/src/vec.rs new file mode 100644 index 0000000..195e207 --- /dev/null +++ b/rust/src/vec.rs @@ -0,0 +1,118 @@ +// https://doc.rust-lang.org/nomicon/vec/vec.html + +use std::alloc::{self, Layout}; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::ptr::{self, NonNull}; + +pub struct Vec { + ptr: NonNull, + cap: usize, + len: usize, + _marker: PhantomData, +} + +unsafe impl Send for Vec {} +unsafe impl Sync for Vec {} + +impl Vec { + pub fn new() -> Self { + assert!(mem::size_of::() != 0, "We're not ready to handle ZSTs"); + Vec { + ptr: NonNull::dangling(), + len: 0, + cap: 0, + _marker: PhantomData, + } + } + + pub fn push(&mut self, elem: T) { + if self.len == self.cap { + self.grow(); + } + + unsafe { + ptr::write(self.ptr.as_ptr().add(self.len), elem); + } + + // Can't fail, we'll OOM first. + self.len += 1; + } + + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + self.len -= 1; + unsafe { Some(ptr::read(self.ptr.as_ptr().add(self.len))) } + } + } + + fn grow(&mut self) { + let (new_cap, new_layout) = if self.cap == 0 { + (1, Layout::array::(1).unwrap()) + } else { + // This can't overflow since self.cap <= isize::MAX. + let new_cap = 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::(new_cap).unwrap(); + (new_cap, new_layout) + }; + + // 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::(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 Drop for Vec { + fn drop(&mut self) { + if self.cap != 0 { + while self.pop().is_some() {} + let layout = Layout::array::(self.cap).unwrap(); + unsafe { + alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); + } + } + } +} + +impl Deref for Vec { + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + } +} + +impl DerefMut for Vec { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + } +} + +impl Default for Vec { + fn default() -> Self { + Self::new() + } +}