main
Alpha Chen 2 years ago
parent f8b030152e
commit eed4628613

@ -1,52 +1,38 @@
use std::alloc::{self, Layout};
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr::{self, NonNull};
#[repr(u8)] #[repr(u8)]
pub enum OpCode { pub enum OpCode {
Return, Return,
} }
type Value = f32;
pub struct Chunk { pub struct Chunk {
count: usize, code: Vec<OpCode>,
capacity: usize, constants: Vec<Value>,
code: NonNull<OpCode>,
_marker: PhantomData<OpCode>,
} }
unsafe impl Send for Chunk {}
unsafe impl Sync for Chunk {}
impl Chunk { impl Chunk {
pub fn new() -> Self { pub fn new() -> Self {
Chunk { Chunk {
count: 0, code: Vec::new(),
capacity: 0, constants: Vec::new(),
code: NonNull::dangling(),
_marker: PhantomData,
} }
} }
// https://doc.rust-lang.org/nomicon/vec/vec-push-pop.html // https://doc.rust-lang.org/nomicon/vec/vec-push-pop.html
pub fn write(&mut self, op_code: OpCode) { pub fn write(&mut self, op_code: OpCode) {
if self.count == self.capacity { self.code.push(op_code);
self.grow(); }
}
unsafe {
ptr::write(self.code.as_ptr().add(self.count), op_code);
}
// Can't fail, we'll OOM first. pub fn add_constant(&mut self, value: Value) -> usize{
self.count += 1; self.constants.push(value);
self.constants.len() - 1
} }
pub fn disassemble(&self, name: &str) { pub fn disassemble(&self, name: &str) {
println!("== {} ==", name); println!("== {} ==", name);
let mut offset = 0; let mut offset = 0;
while offset < self.count { while offset < self.code.len() {
offset = self.disassemble_instruction(offset); offset = self.disassemble_instruction(offset);
} }
} }
@ -54,7 +40,7 @@ impl Chunk {
fn disassemble_instruction(&self, offset: usize) -> usize { fn disassemble_instruction(&self, offset: usize) -> usize {
print!("{:04} ", offset); print!("{:04} ", offset);
match self[offset] { match self.code[offset] {
OpCode::Return => self.simple_instruction("OP_RETURN", offset), OpCode::Return => self.simple_instruction("OP_RETURN", offset),
} }
} }
@ -63,57 +49,6 @@ impl Chunk {
println!("{}", name); println!("{}", name);
offset + 1 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 { impl Default for Chunk {
@ -121,23 +56,3 @@ impl Default for Chunk {
Self::new() 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) }
}
}

@ -1,6 +1,7 @@
use chunk::{Chunk, OpCode}; use chunk::{Chunk, OpCode};
pub mod chunk; pub mod chunk;
pub mod vec;
fn main() { fn main() {
let mut chunk = Chunk::default(); let mut chunk = Chunk::default();

@ -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()
}
}
Loading…
Cancel
Save