diff --git a/frontend/src/HeapGrid.tsx b/frontend/src/HeapGrid.tsx index d33a1f7..7ea8ebd 100644 --- a/frontend/src/HeapGrid.tsx +++ b/frontend/src/HeapGrid.tsx @@ -11,7 +11,6 @@ interface HeapGridProps { const cellStyleMap: Record = { [CellStatus.Free]: 'white', - [CellStatus.ToBeFree]: 'magenta', [CellStatus.Allocated]: '#7CCD7C', [CellStatus.Marked]: 'yellow', [CellStatus.Used]: '#228B22', diff --git a/frontend/src/Visualization.tsx b/frontend/src/Visualization.tsx index 5070f25..2943568 100644 --- a/frontend/src/Visualization.tsx +++ b/frontend/src/Visualization.tsx @@ -141,7 +141,7 @@ const Visualization: React.FC = () => { if (pendingGCEvents.length > 0) { const alterHeapState = (event: GCEvent): void => { if (event.type === 'FreeObject') { - const cellIndex: number = cellIndexFromEvent(event)!; + const [cellIndex] = cellIndexesFromEvent(event)!; setMemory(memory.map((c, i) => { if (i === cellIndex) { c.status = CellStatus.Free; @@ -149,20 +149,30 @@ const Visualization: React.FC = () => { return c })); } else if (event.type === 'MarkObject') { - const cellIndex: number = cellIndexFromEvent(event)!; + const [cellIndex] = cellIndexesFromEvent(event)!; setMemory(memory.map((c, i) => { if (i === cellIndex) { c.status = CellStatus.Marked; } return c })); + } else if (event.type === 'MoveObject') { + const { from, to, size } = event; + setMemory(memory.map((c, i) => { + if (i >= from && i < from + size) { + c.status = CellStatus.Free; + } else if (i >= to && i < to + size) { + c.status = CellStatus.Allocated; + } + return c; + })); } } const currentGCEvent: GCEvent = pendingGCEvents[0]; setGCEventLogs(prevLogs => [...prevLogs, currentGCEvent]); if (eventHasAnimation(currentGCEvent)) { - const cellIndex: number = cellIndexFromEvent(currentGCEvent)!; - enqueueAnimation(cellIndex, animationFromGCEvent(currentGCEvent)); + const cellIndexes: number[] = cellIndexesFromEvent(currentGCEvent)!; + enqueueAnimation(cellIndexes, animationFromGCEvent(currentGCEvent)); } // Update heap cell state if needed. @@ -288,14 +298,18 @@ const animationFromGCEvent = (event: GCEvent): TimedAnimation => { return animation; } -const cellIndexFromEvent = (event: GCEvent): number | null => { +const cellIndexesFromEvent = (event: GCEvent): number[] => { switch (event.type) { case 'MarkObject': case 'FreeObject': - return event.addr; + return [event.addr]; + case 'MoveObject': + return [event.from, event.to]; + case 'UpdateFwdPtr': + return [event.old, event.new]; case 'GCPhase': default: - return null; + return []; } }; diff --git a/frontend/src/eventlog.tsx b/frontend/src/eventlog.tsx index 6654b17..1546d47 100644 --- a/frontend/src/eventlog.tsx +++ b/frontend/src/eventlog.tsx @@ -38,6 +38,8 @@ export const gcEventOps = (event: GCEvent): EventOps => ({ case "MarkObject": case "FreeObject": return [event.addr]; + case "MoveObject": + return Array.from({ length: event.size }, (_, i) => event.to + i); default: return []; } @@ -54,6 +56,9 @@ export const gcEventOps = (event: GCEvent): EventOps => ({ case "FreeObject": message = `Freed Object at address 0x${event.addr.toString(16)}`; break; + case "MoveObject": + message = `Move object 0x${event.from.toString(16)} -> 0x${event.to.toString(16)}`; + break; case "UpdateFwdPtr": message = `Update Forward Pointer 0x${event.old.toString(16)} -> 0x${event.new.toString(16)}`; break; diff --git a/frontend/src/types.tsx b/frontend/src/types.tsx index 3cf8967..52dae0b 100644 --- a/frontend/src/types.tsx +++ b/frontend/src/types.tsx @@ -1,6 +1,5 @@ export enum CellStatus { Free = "Free", - ToBeFree = "ToBeFree", Allocated = "Allocated", Marked = "Marked", Used = "Used" @@ -71,4 +70,5 @@ export type GCEvent = | { type: "GCPhase", msg: string } | { type: "MarkObject", addr: number, size: number } | { type: "FreeObject", addr: number, size: number } + | { type: "MoveObject", from: number, to: number, size: number } | { type: "UpdateFwdPtr", old: number, new: number }; diff --git a/frontend/src/useHeapAnimation.tsx b/frontend/src/useHeapAnimation.tsx index 155e1a2..16f96e3 100644 --- a/frontend/src/useHeapAnimation.tsx +++ b/frontend/src/useHeapAnimation.tsx @@ -39,18 +39,23 @@ const useHeapAnimation = () => { setHighlightedCells([]); }; - const enqueueAnimation = (cellIndex: number, animation: TimedAnimation) => { - // Remove any existing animation for this cell - setAnimatedCells(prevState => prevState.filter(cell => cell.cellIndex !== cellIndex)); - setAnimatedCells(prevState => [...prevState, { cellIndex, animation }]); + const enqueueAnimation = (cellIndexes: number[], animation: TimedAnimation) => { + // Remove any existing animations for these cells + setAnimatedCells(prevState => prevState.filter(cell => !cellIndexes.includes(cell.cellIndex))); - // Set timeout to clear the animation after its duration - const timeout = window.setTimeout(() => { - setAnimatedCells(prevState => prevState.filter(cell => cell.cellIndex !== cellIndex)); - }, animation.duration); + // Add new animations for these cells + const newAnimations: AnimatedCell[] = cellIndexes.map(cellIndex => ({ cellIndex, animation })); + setAnimatedCells(prevState => [...prevState, ...newAnimations]); - setAnimationTimeouts(prevState => [...prevState, timeout]); - }; + // Set timeouts to clear the animations after their duration + const timeouts = cellIndexes.map(cellIndex => { + return window.setTimeout(() => { + setAnimatedCells(prevState => prevState.filter(cell => cell.cellIndex !== cellIndex)); + }, animation.duration); + }); + + setAnimationTimeouts(prevState => [...prevState, ...timeouts]); + } const clearAnimations = () => { // Clear all animated cells diff --git a/src/gc/compact/two_finger.rs b/src/gc/compact/two_finger.rs index bb2bc74..018080d 100644 --- a/src/gc/compact/two_finger.rs +++ b/src/gc/compact/two_finger.rs @@ -10,13 +10,10 @@ use crate::{ }; // Two-Finger Algorithm, -// introduced by W. L. Edwards in 1974 +// introduced by Edwards in 1974 pub fn compact(heap: &mut Heap, eventlog: &mut Vec) { let start: usize = 0; let end: usize = heap.last_object_addr().unwrap(); - eventlog.push(GCEvent::phase(format!( - "Run 2 finger compaction. Start: {start} End: {end}" - ))); let forwarding_ptrs = relocate(heap, eventlog, start, end); update_references(heap, &forwarding_ptrs, eventlog); } @@ -104,7 +101,8 @@ fn can_fit_into(heap: &Heap, move_in: ObjAddr, dest: ObjAddr) -> bool { fn move_object(heap: &mut Heap, eventlog: &mut Vec, from: ObjAddr, to: ObjAddr) { if heap.objects.get(&from).is_some() { - eventlog.push(GCEvent::phase(format!("Moving object from {from} to {to}"))); + let size = heap.objects.get(&from).unwrap().size(); + eventlog.push(GCEvent::MoveObject{from, to, size}); match heap.move_object(from, to) { Ok(res) => res, Err(_e) => panic!("move_object {_e}"), diff --git a/src/gc/mod.rs b/src/gc/mod.rs index 135937c..ca6337e 100644 --- a/src/gc/mod.rs +++ b/src/gc/mod.rs @@ -66,6 +66,7 @@ pub enum GCEvent { GCPhase { msg: String }, MarkObject { addr: usize, size: usize }, FreeObject { addr: usize, size: usize }, + MoveObject { from: usize, to: usize, size: usize }, UpdateFwdPtr { old: usize, new: usize }, } diff --git a/src/heap.rs b/src/heap.rs index f3952f6..2a99b6e 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -36,7 +36,6 @@ impl MemoryCell { #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum CellStatus { Free, - ToBeFree, Allocated, Marked, Used,