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<OpCode>,
- _marker: PhantomData<OpCode>,
+ code: Vec<OpCode>,
+ constants: Vec<Value>,
}
-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::<u8>(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::<u8>(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<OpCode> {
- 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::<u8>(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<T> {
+ ptr: NonNull<T>,
+ cap: usize,
+ len: usize,
+ _marker: PhantomData<T>,
+}
+
+unsafe impl<T: Send> Send for Vec<T> {}
+unsafe impl<T: Sync> Sync for Vec<T> {}
+
+impl<T> Vec<T> {
+ pub fn new() -> Self {
+ assert!(mem::size_of::<T>() != 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<T> {
+ 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::<T>(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::<T>(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::<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 Vec<T> {
+ fn drop(&mut self) {
+ if self.cap != 0 {
+ while self.pop().is_some() {}
+ let layout = Layout::array::<T>(self.cap).unwrap();
+ unsafe {
+ alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
+ }
+ }
+ }
+}
+
+impl<T> Deref for Vec<T> {
+ type Target = [T];
+ fn deref(&self) -> &[T] {
+ unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
+ }
+}
+
+impl<T> DerefMut for Vec<T> {
+ fn deref_mut(&mut self) -> &mut [T] {
+ unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
+ }
+}
+
+impl<T> Default for Vec<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}