1use std::fmt;
19
20use serde::{Deserialize, Serialize};
21
22use super::super::file::id::FileId;
23use super::super::node::id::{GenerationOverflowError, NodeId};
24use super::super::node::kind::NodeKind;
25use super::super::string::id::StringId;
26use crate::graph::body_hash::BodyHash128;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum ArenaError {
34 GenerationOverflow(GenerationOverflowError),
36
37 FreeListCorruption {
43 index: u32,
45 },
46
47 CapacityExceeded,
49}
50
51impl fmt::Display for ArenaError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 ArenaError::GenerationOverflow(e) => write!(f, "{e}"),
55 ArenaError::FreeListCorruption { index } => {
56 write!(
57 f,
58 "free list corruption: occupied slot found at index {index}"
59 )
60 }
61 ArenaError::CapacityExceeded => {
62 write!(
63 f,
64 "arena capacity exceeded: cannot allocate more than {} nodes",
65 u32::MAX
66 )
67 }
68 }
69 }
70}
71
72impl std::error::Error for ArenaError {
73 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74 match self {
75 ArenaError::GenerationOverflow(e) => Some(e),
76 _ => None,
77 }
78 }
79}
80
81impl From<GenerationOverflowError> for ArenaError {
82 fn from(e: GenerationOverflowError) -> Self {
83 ArenaError::GenerationOverflow(e)
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub enum SlotState<T> {
90 Occupied(T),
92 Vacant {
94 next_free: Option<u32>,
96 },
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct Slot<T> {
102 generation: u64,
104 state: SlotState<T>,
106}
107
108impl<T> Slot<T> {
109 #[allow(dead_code)] fn new_vacant(generation: u64, next_free: Option<u32>) -> Self {
112 Self {
113 generation,
114 state: SlotState::Vacant { next_free },
115 }
116 }
117
118 pub(crate) fn new_occupied(generation: u64, data: T) -> Self {
120 Self {
121 generation,
122 state: SlotState::Occupied(data),
123 }
124 }
125
126 #[inline]
128 pub fn is_occupied(&self) -> bool {
129 matches!(self.state, SlotState::Occupied(_))
130 }
131
132 #[inline]
134 pub fn is_vacant(&self) -> bool {
135 matches!(self.state, SlotState::Vacant { .. })
136 }
137
138 #[inline]
140 pub fn generation(&self) -> u64 {
141 self.generation
142 }
143
144 pub fn get(&self) -> Option<&T> {
146 match &self.state {
147 SlotState::Occupied(data) => Some(data),
148 SlotState::Vacant { .. } => None,
149 }
150 }
151
152 #[inline]
154 pub fn state(&self) -> &SlotState<T> {
155 &self.state
156 }
157
158 pub fn get_mut(&mut self) -> Option<&mut T> {
160 match &mut self.state {
161 SlotState::Occupied(data) => Some(data),
162 SlotState::Vacant { .. } => None,
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct NodeEntry {
172 pub kind: NodeKind,
174 pub name: StringId,
176 pub file: FileId,
178 pub start_byte: u32,
180 pub end_byte: u32,
182 pub start_line: u32,
184 pub start_column: u32,
186 pub end_line: u32,
188 pub end_column: u32,
190 pub signature: Option<StringId>,
192 pub doc: Option<StringId>,
194 pub qualified_name: Option<StringId>,
196 pub visibility: Option<StringId>,
198 pub is_async: bool,
200 pub is_static: bool,
202 #[serde(default)]
212 pub body_hash: Option<BodyHash128>,
213 #[serde(default)]
224 pub is_unsafe: bool,
225}
226
227impl NodeEntry {
228 #[must_use]
230 pub fn new(kind: NodeKind, name: StringId, file: FileId) -> Self {
231 Self {
232 kind,
233 name,
234 file,
235 start_byte: 0,
236 end_byte: 0,
237 start_line: 0,
238 start_column: 0,
239 end_line: 0,
240 end_column: 0,
241 signature: None,
242 doc: None,
243 qualified_name: None,
244 visibility: None,
245 is_async: false,
246 is_static: false,
247 is_unsafe: false,
248 body_hash: None,
249 }
250 }
251
252 #[must_use]
254 pub fn with_byte_range(mut self, start: u32, end: u32) -> Self {
255 self.start_byte = start;
256 self.end_byte = end;
257 self
258 }
259
260 #[must_use]
262 pub fn with_location(
263 mut self,
264 start_line: u32,
265 start_column: u32,
266 end_line: u32,
267 end_column: u32,
268 ) -> Self {
269 self.start_line = start_line;
270 self.start_column = start_column;
271 self.end_line = end_line;
272 self.end_column = end_column;
273 self
274 }
275
276 #[must_use]
278 pub fn with_signature(mut self, signature: StringId) -> Self {
279 self.signature = Some(signature);
280 self
281 }
282
283 #[must_use]
285 pub fn with_doc(mut self, doc: StringId) -> Self {
286 self.doc = Some(doc);
287 self
288 }
289
290 #[must_use]
292 pub fn with_qualified_name(mut self, qualified_name: StringId) -> Self {
293 self.qualified_name = Some(qualified_name);
294 self
295 }
296
297 #[must_use]
299 pub fn with_visibility(mut self, visibility: StringId) -> Self {
300 self.visibility = Some(visibility);
301 self
302 }
303
304 #[must_use]
306 pub fn with_async(mut self, is_async: bool) -> Self {
307 self.is_async = is_async;
308 self
309 }
310
311 #[must_use]
313 pub fn with_static(mut self, is_static: bool) -> Self {
314 self.is_static = is_static;
315 self
316 }
317
318 #[must_use]
320 pub fn with_unsafe(mut self, is_unsafe: bool) -> Self {
321 self.is_unsafe = is_unsafe;
322 self
323 }
324
325 #[must_use]
330 pub fn with_body_hash(mut self, hash: BodyHash128) -> Self {
331 self.body_hash = Some(hash);
332 self
333 }
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct NodeArena {
360 slots: Vec<Slot<NodeEntry>>,
362 free_head: Option<u32>,
364 len: usize,
366}
367
368impl NodeArena {
369 #[must_use]
371 pub fn new() -> Self {
372 Self {
373 slots: Vec::new(),
374 free_head: None,
375 len: 0,
376 }
377 }
378
379 #[must_use]
381 pub fn with_capacity(capacity: usize) -> Self {
382 Self {
383 slots: Vec::with_capacity(capacity),
384 free_head: None,
385 len: 0,
386 }
387 }
388
389 #[inline]
391 #[must_use]
392 pub fn len(&self) -> usize {
393 self.len
394 }
395
396 #[inline]
398 #[must_use]
399 pub fn is_empty(&self) -> bool {
400 self.len == 0
401 }
402
403 #[inline]
405 #[must_use]
406 pub fn capacity(&self) -> usize {
407 self.slots.capacity()
408 }
409
410 #[inline]
415 #[must_use]
416 pub fn slot_count(&self) -> usize {
417 self.slots.len()
418 }
419
420 pub fn alloc(&mut self, entry: NodeEntry) -> Result<NodeId, ArenaError> {
432 self.len += 1;
433
434 if let Some(free_idx) = self.free_head {
435 let slot_index = free_idx as usize;
437 if slot_index >= self.slots.len() {
438 self.len -= 1; return Err(ArenaError::FreeListCorruption { index: free_idx });
440 }
441
442 let slot = &mut self.slots[slot_index];
444
445 self.free_head = match &slot.state {
447 SlotState::Vacant { next_free } => *next_free,
448 SlotState::Occupied(_) => {
449 self.len -= 1; return Err(ArenaError::FreeListCorruption { index: free_idx });
452 }
453 };
454
455 let temp_id = NodeId::new(free_idx, slot.generation);
458 let new_generation = temp_id.try_increment_generation().map_err(|mut e| {
459 self.len -= 1; e.index = free_idx; ArenaError::GenerationOverflow(e)
462 })?;
463
464 slot.generation = new_generation;
465 slot.state = SlotState::Occupied(entry);
466
467 Ok(NodeId::new(free_idx, new_generation))
468 } else {
469 let index = self.slots.len();
471
472 let Ok(index_u32) = u32::try_from(index) else {
474 self.len -= 1; return Err(ArenaError::CapacityExceeded);
476 };
477
478 let slot = Slot::new_occupied(1, entry);
479 self.slots.push(slot);
480
481 Ok(NodeId::new(index_u32, 1))
482 }
483 }
484
485 #[must_use]
489 pub fn get(&self, id: NodeId) -> Option<&NodeEntry> {
490 if id.is_invalid() {
491 return None;
492 }
493
494 let index = id.index() as usize;
495 let slot = self.slots.get(index)?;
496
497 if slot.generation != id.generation() {
499 return None;
500 }
501
502 slot.get()
503 }
504
505 #[must_use]
509 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut NodeEntry> {
510 if id.is_invalid() {
511 return None;
512 }
513
514 let index = id.index() as usize;
515 let slot = self.slots.get_mut(index)?;
516
517 if slot.generation != id.generation() {
519 return None;
520 }
521
522 slot.get_mut()
523 }
524
525 #[must_use]
527 pub fn contains(&self, id: NodeId) -> bool {
528 self.get(id).is_some()
529 }
530
531 pub fn remove(&mut self, id: NodeId) -> Option<NodeEntry> {
537 if id.is_invalid() {
538 return None;
539 }
540
541 let index = id.index() as usize;
542 let slot = self.slots.get_mut(index)?;
543
544 if slot.generation != id.generation() {
546 return None;
547 }
548
549 if let SlotState::Occupied(_) = &slot.state {
552 let old_state = std::mem::replace(
553 &mut slot.state,
554 SlotState::Vacant {
555 next_free: self.free_head,
556 },
557 );
558
559 self.free_head = Some(id.index());
561 self.len -= 1;
562
563 if let SlotState::Occupied(entry) = old_state {
564 return Some(entry);
565 }
566 }
567
568 None
569 }
570
571 pub fn iter(&self) -> impl Iterator<Item = (NodeId, &NodeEntry)> {
573 self.slots.iter().enumerate().filter_map(|(index, slot)| {
574 if let SlotState::Occupied(entry) = &slot.state {
575 let index_u32 = u32::try_from(index).ok()?;
576 Some((NodeId::new(index_u32, slot.generation), entry))
577 } else {
578 None
579 }
580 })
581 }
582
583 pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut NodeEntry)> {
585 self.slots
586 .iter_mut()
587 .enumerate()
588 .filter_map(|(index, slot)| {
589 let generation = slot.generation;
590 if let SlotState::Occupied(entry) = &mut slot.state {
591 let index_u32 = u32::try_from(index).ok()?;
592 Some((NodeId::new(index_u32, generation), entry))
593 } else {
594 None
595 }
596 })
597 }
598
599 pub fn clear(&mut self) {
603 self.slots.clear();
604 self.free_head = None;
605 self.len = 0;
606 }
607
608 pub fn reserve(&mut self, additional: usize) {
610 self.slots.reserve(additional);
611 }
612
613 #[must_use]
617 pub fn slot(&self, index: u32) -> Option<&Slot<NodeEntry>> {
618 self.slots.get(index as usize)
619 }
620
621 pub fn alloc_range(&mut self, count: u32, placeholder: &NodeEntry) -> Result<u32, ArenaError> {
635 if count == 0 {
636 return Ok(u32::try_from(self.slots.len())
639 .unwrap_or_else(|_| unreachable!("slot_count invariant violated")));
640 }
641
642 let start = self.slots.len();
643 let new_total = start
644 .checked_add(count as usize)
645 .ok_or(ArenaError::CapacityExceeded)?;
646
647 if new_total > u32::MAX as usize + 1 {
649 return Err(ArenaError::CapacityExceeded);
650 }
651
652 let start_u32 = u32::try_from(start).map_err(|_| ArenaError::CapacityExceeded)?;
653
654 self.slots.reserve(count as usize);
656 for _ in 0..count {
657 self.slots.push(Slot::new_occupied(1, placeholder.clone()));
658 }
659
660 self.len += count as usize;
662
663 Ok(start_u32)
664 }
665
666 #[must_use]
675 pub fn bulk_slice_mut(&mut self, start: u32, count: u32) -> &mut [Slot<NodeEntry>] {
676 let begin = start as usize;
677 let end = begin + count as usize;
678 &mut self.slots[begin..end]
679 }
680
681 pub fn truncate_to(&mut self, saved_slot_count: usize) {
692 assert!(
693 saved_slot_count <= self.slots.len(),
694 "truncate_to({saved_slot_count}) exceeds current slot count ({})",
695 self.slots.len(),
696 );
697
698 let dropped_occupied = self.slots[saved_slot_count..]
700 .iter()
701 .filter(|s| s.is_occupied())
702 .count();
703
704 self.slots.truncate(saved_slot_count);
705 self.len -= dropped_occupied;
706
707 self.rebuild_free_list_after_truncate(saved_slot_count);
710 }
711
712 fn rebuild_free_list_after_truncate(&mut self, cutoff: usize) {
718 let mut new_head: Option<u32> = None;
722 for i in (0..cutoff).rev() {
725 if self.slots[i].is_vacant() {
726 let i_u32 = u32::try_from(i)
727 .unwrap_or_else(|_| unreachable!("free-list index exceeds u32 invariant"));
728 self.slots[i].state = SlotState::Vacant {
729 next_free: new_head,
730 };
731 new_head = Some(i_u32);
732 }
733 }
734 self.free_head = new_head;
735 }
736
737 #[must_use]
742 pub fn slots(&self) -> &[Slot<NodeEntry>] {
743 &self.slots
744 }
745}
746
747impl Default for NodeArena {
748 fn default() -> Self {
749 Self::new()
750 }
751}
752
753impl fmt::Display for NodeArena {
754 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
755 write!(
756 f,
757 "NodeArena(len={}, capacity={})",
758 self.len,
759 self.capacity()
760 )
761 }
762}
763
764impl crate::graph::unified::memory::GraphMemorySize for NodeArena {
765 fn heap_bytes(&self) -> usize {
766 self.slots.capacity() * std::mem::size_of::<Slot<NodeEntry>>()
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773
774 fn test_file() -> FileId {
775 FileId::new(1)
776 }
777
778 fn test_name() -> StringId {
779 StringId::new(1)
780 }
781
782 fn test_entry(kind: NodeKind) -> NodeEntry {
783 NodeEntry::new(kind, test_name(), test_file())
784 }
785
786 #[test]
787 fn test_arena_new() {
788 let arena = NodeArena::new();
789 assert_eq!(arena.len(), 0);
790 assert!(arena.is_empty());
791 assert_eq!(arena.capacity(), 0);
792 }
793
794 #[test]
795 fn test_arena_with_capacity() {
796 let arena = NodeArena::with_capacity(100);
797 assert_eq!(arena.len(), 0);
798 assert!(arena.capacity() >= 100);
799 }
800
801 #[test]
802 fn test_alloc_and_get() {
803 let mut arena = NodeArena::new();
804 let entry = test_entry(NodeKind::Function);
805
806 let id = arena.alloc(entry).unwrap();
807 assert!(!id.is_invalid());
808 assert_eq!(arena.len(), 1);
809
810 let node = arena.get(id).unwrap();
811 assert_eq!(node.kind, NodeKind::Function);
812 }
813
814 #[test]
815 fn test_alloc_multiple() {
816 let mut arena = NodeArena::new();
817
818 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
819 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
820 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
821
822 assert_eq!(arena.len(), 3);
823 assert_ne!(id1, id2);
824 assert_ne!(id2, id3);
825
826 assert_eq!(arena.get(id1).unwrap().kind, NodeKind::Function);
827 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Class);
828 assert_eq!(arena.get(id3).unwrap().kind, NodeKind::Method);
829 }
830
831 #[test]
832 fn test_get_mut() {
833 let mut arena = NodeArena::new();
834 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
835
836 {
837 let node = arena.get_mut(id).unwrap();
838 node.start_line = 42;
839 }
840
841 assert_eq!(arena.get(id).unwrap().start_line, 42);
842 }
843
844 #[test]
845 fn test_contains() {
846 let mut arena = NodeArena::new();
847 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
848
849 assert!(arena.contains(id));
850 assert!(!arena.contains(NodeId::INVALID));
851 }
852
853 #[test]
854 fn test_remove() {
855 let mut arena = NodeArena::new();
856 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
857
858 assert_eq!(arena.len(), 1);
859 assert!(arena.contains(id));
860
861 let removed = arena.remove(id).unwrap();
862 assert_eq!(removed.kind, NodeKind::Function);
863 assert_eq!(arena.len(), 0);
864 assert!(!arena.contains(id));
865 }
866
867 #[test]
868 fn test_stale_id_after_remove() {
869 let mut arena = NodeArena::new();
870 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
871
872 arena.remove(id1);
874 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
875
876 assert!(arena.get(id1).is_none());
878 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Class);
880 assert_eq!(id1.index(), id2.index());
882 assert_ne!(id1.generation(), id2.generation());
883 }
884
885 #[test]
886 fn test_remove_idempotent() {
887 let mut arena = NodeArena::new();
888 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
889 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
890
891 assert!(arena.remove(id1).is_some());
893 assert_eq!(arena.len(), 1);
894
895 assert!(arena.remove(id1).is_none());
897 assert_eq!(arena.len(), 1); assert!(arena.remove(id1).is_none());
901 assert_eq!(arena.len(), 1); assert!(arena.contains(id2));
905
906 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
908 assert_eq!(id3.index(), id1.index()); assert_eq!(arena.len(), 2);
910 }
911
912 #[test]
913 fn test_free_list_reuse() {
914 let mut arena = NodeArena::new();
915
916 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
918 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
919 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
920
921 arena.remove(id2);
923 assert_eq!(arena.len(), 2);
924 assert_eq!(arena.slot_count(), 3);
925
926 let id4 = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
928 assert_eq!(id4.index(), id2.index());
929 assert_eq!(arena.len(), 3);
930 assert_eq!(arena.slot_count(), 3); assert!(arena.get(id2).is_none());
934 assert_eq!(arena.get(id4).unwrap().kind, NodeKind::Variable);
936 assert!(arena.contains(id1));
938 assert!(arena.contains(id3));
939 }
940
941 #[test]
942 fn test_invalid_id() {
943 let arena = NodeArena::new();
944 assert!(arena.get(NodeId::INVALID).is_none());
945 }
946
947 #[test]
948 fn test_out_of_bounds_id() {
949 let arena = NodeArena::new();
950 let fake_id = NodeId::new(999, 1);
951 assert!(arena.get(fake_id).is_none());
952 }
953
954 #[test]
955 fn test_wrong_generation() {
956 let mut arena = NodeArena::new();
957 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
958
959 let wrong_gen_id = NodeId::new(id.index(), id.generation() + 1);
961 assert!(arena.get(wrong_gen_id).is_none());
962 }
963
964 #[test]
965 fn test_iter() {
966 let mut arena = NodeArena::new();
967 arena.alloc(test_entry(NodeKind::Function)).unwrap();
968 arena.alloc(test_entry(NodeKind::Class)).unwrap();
969 arena.alloc(test_entry(NodeKind::Method)).unwrap();
970
971 let entries: Vec<_> = arena.iter().collect();
972 assert_eq!(entries.len(), 3);
973
974 let kinds: Vec<_> = entries.iter().map(|(_, e)| e.kind).collect();
975 assert!(kinds.contains(&NodeKind::Function));
976 assert!(kinds.contains(&NodeKind::Class));
977 assert!(kinds.contains(&NodeKind::Method));
978 }
979
980 #[test]
981 fn test_iter_with_removed() {
982 let mut arena = NodeArena::new();
983 let id1 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
984 let id2 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
985 let id3 = arena.alloc(test_entry(NodeKind::Method)).unwrap();
986
987 arena.remove(id2);
988
989 let entries: Vec<_> = arena.iter().collect();
990 assert_eq!(entries.len(), 2);
991
992 let collected_ids: Vec<_> = entries.iter().map(|(id, _)| *id).collect();
993 assert!(collected_ids.contains(&id1));
994 assert!(!collected_ids.contains(&id2));
995 assert!(collected_ids.contains(&id3));
996 }
997
998 #[test]
999 fn test_iter_mut() {
1000 let mut arena = NodeArena::new();
1001 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1002 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1003
1004 for (_, entry) in arena.iter_mut() {
1005 entry.start_line = 100;
1006 }
1007
1008 for (_, entry) in arena.iter() {
1009 assert_eq!(entry.start_line, 100);
1010 }
1011 }
1012
1013 #[test]
1014 fn test_clear() {
1015 let mut arena = NodeArena::new();
1016 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1017 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1018
1019 assert_eq!(arena.len(), 2);
1020 arena.clear();
1021 assert_eq!(arena.len(), 0);
1022 assert!(arena.is_empty());
1023 }
1024
1025 #[test]
1026 fn test_reserve() {
1027 let mut arena = NodeArena::new();
1028 arena.reserve(1000);
1029 assert!(arena.capacity() >= 1000);
1030 }
1031
1032 #[test]
1033 fn test_display() {
1034 let mut arena = NodeArena::new();
1035 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1036
1037 let display = format!("{arena}");
1038 assert!(display.contains("NodeArena"));
1039 assert!(display.contains("len=1"));
1040 }
1041
1042 #[test]
1043 fn test_node_entry_builder() {
1044 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file())
1045 .with_byte_range(10, 100)
1046 .with_location(1, 0, 5, 10)
1047 .with_signature(StringId::new(2))
1048 .with_doc(StringId::new(3))
1049 .with_qualified_name(StringId::new(4));
1050
1051 assert_eq!(entry.kind, NodeKind::Function);
1052 assert_eq!(entry.start_byte, 10);
1053 assert_eq!(entry.end_byte, 100);
1054 assert_eq!(entry.start_line, 1);
1055 assert_eq!(entry.start_column, 0);
1056 assert_eq!(entry.end_line, 5);
1057 assert_eq!(entry.end_column, 10);
1058 assert_eq!(entry.signature, Some(StringId::new(2)));
1059 assert_eq!(entry.doc, Some(StringId::new(3)));
1060 assert_eq!(entry.qualified_name, Some(StringId::new(4)));
1061 }
1062
1063 #[test]
1064 fn test_slot_state() {
1065 let occupied: Slot<i32> = Slot::new_occupied(1, 42);
1066 assert!(occupied.is_occupied());
1067 assert!(!occupied.is_vacant());
1068 assert_eq!(occupied.get(), Some(&42));
1069
1070 let vacant: Slot<i32> = Slot::new_vacant(2, Some(5));
1071 assert!(!vacant.is_occupied());
1072 assert!(vacant.is_vacant());
1073 assert_eq!(vacant.get(), None);
1074 }
1075
1076 #[test]
1077 fn test_slot_generation() {
1078 let slot: Slot<i32> = Slot::new_occupied(42, 100);
1079 assert_eq!(slot.generation(), 42);
1080 }
1081
1082 #[test]
1083 fn test_slot_state_accessor() {
1084 let occupied: Slot<i32> = Slot::new_occupied(1, 42);
1085 assert!(matches!(occupied.state(), SlotState::Occupied(42)));
1086
1087 let vacant: Slot<i32> = Slot::new_vacant(1, Some(3));
1088 assert!(matches!(
1089 vacant.state(),
1090 SlotState::Vacant { next_free: Some(3) }
1091 ));
1092 }
1093
1094 #[test]
1097 fn test_alloc_range_basic() {
1098 let mut arena = NodeArena::new();
1099 let placeholder = test_entry(NodeKind::Function);
1100
1101 let start = arena.alloc_range(5, &placeholder).unwrap();
1102 assert_eq!(start, 0);
1103 assert_eq!(arena.len(), 5);
1104 assert_eq!(arena.slot_count(), 5);
1105
1106 for i in 0..5u32 {
1108 let id = NodeId::new(i, 1);
1109 let entry = arena.get(id).expect("slot should be occupied");
1110 assert_eq!(entry.kind, NodeKind::Function);
1111 }
1112 }
1113
1114 #[test]
1115 fn test_alloc_range_after_existing() {
1116 let mut arena = NodeArena::new();
1117
1118 let id0 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1120 assert_eq!(id0.index(), 0);
1121 assert_eq!(arena.len(), 1);
1122
1123 let placeholder = test_entry(NodeKind::Method);
1125 let start = arena.alloc_range(3, &placeholder).unwrap();
1126 assert_eq!(start, 1);
1127 assert_eq!(arena.len(), 4);
1128 assert_eq!(arena.slot_count(), 4);
1129
1130 assert_eq!(arena.get(id0).unwrap().kind, NodeKind::Class);
1132
1133 for i in 1..4u32 {
1135 let id = NodeId::new(i, 1);
1136 assert_eq!(arena.get(id).unwrap().kind, NodeKind::Method);
1137 }
1138 }
1139
1140 #[test]
1141 fn test_alloc_range_zero_is_noop() {
1142 let mut arena = NodeArena::new();
1143
1144 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1146 let len_before = arena.len();
1147 let slot_count_before = arena.slot_count();
1148
1149 let start = arena.alloc_range(0, &test_entry(NodeKind::Class)).unwrap();
1150 #[allow(clippy::cast_possible_truncation)]
1151 let expected_start = slot_count_before as u32;
1153 assert_eq!(start, expected_start);
1154 assert_eq!(arena.len(), len_before);
1155 assert_eq!(arena.slot_count(), slot_count_before);
1156 }
1157
1158 #[test]
1159 fn test_bulk_slice_mut() {
1160 let mut arena = NodeArena::new();
1161 let placeholder = test_entry(NodeKind::Function);
1162 let start = arena.alloc_range(3, &placeholder).unwrap();
1163
1164 {
1166 let slice = arena.bulk_slice_mut(start, 3);
1167 assert_eq!(slice.len(), 3);
1168
1169 slice[1] = Slot::new_occupied(1, test_entry(NodeKind::Class));
1171 }
1172
1173 let id1 = NodeId::new(1, 1);
1175 assert_eq!(arena.get(id1).unwrap().kind, NodeKind::Class);
1176
1177 let id0 = NodeId::new(0, 1);
1179 assert_eq!(arena.get(id0).unwrap().kind, NodeKind::Function);
1180 let id2 = NodeId::new(2, 1);
1181 assert_eq!(arena.get(id2).unwrap().kind, NodeKind::Function);
1182 }
1183
1184 #[test]
1185 fn test_truncate_to() {
1186 let mut arena = NodeArena::new();
1187 let placeholder = test_entry(NodeKind::Function);
1188
1189 arena.alloc_range(3, &placeholder).unwrap();
1191 assert_eq!(arena.len(), 3);
1192 assert_eq!(arena.slot_count(), 3);
1193
1194 let saved = arena.slot_count();
1196
1197 arena.alloc_range(4, &placeholder).unwrap();
1199 assert_eq!(arena.len(), 7);
1200 assert_eq!(arena.slot_count(), 7);
1201
1202 arena.truncate_to(saved);
1204 assert_eq!(arena.len(), 3);
1205 assert_eq!(arena.slot_count(), 3);
1206
1207 for i in 0..3u32 {
1209 let id = NodeId::new(i, 1);
1210 assert!(arena.get(id).is_some());
1211 }
1212 }
1213
1214 #[test]
1215 fn test_alloc_range_with_free_list() {
1216 let mut arena = NodeArena::new();
1217
1218 let id0 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1220 let _id1 = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1221 arena.remove(id0);
1222
1223 assert_eq!(arena.len(), 1);
1225 assert_eq!(arena.slot_count(), 2);
1226
1227 let start = arena.alloc_range(3, &test_entry(NodeKind::Method)).unwrap();
1229 assert_eq!(start, 2); assert_eq!(arena.len(), 4); assert_eq!(arena.slot_count(), 5); let slot0 = arena.slot(0).unwrap();
1235 assert!(slot0.is_vacant());
1236
1237 for i in 2..5u32 {
1239 let id = NodeId::new(i, 1);
1240 assert_eq!(arena.get(id).unwrap().kind, NodeKind::Method);
1241 }
1242 }
1243
1244 #[test]
1245 fn test_truncate_to_clamps_free_head() {
1246 let mut arena = NodeArena::new();
1247
1248 let mut ids = Vec::new();
1250 for _ in 0..5 {
1251 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1252 }
1253
1254 let saved = arena.slot_count();
1256
1257 let extra_ids: Vec<_> = (0..3)
1259 .map(|_| arena.alloc(test_entry(NodeKind::Class)).unwrap())
1260 .collect();
1261 arena.remove(extra_ids[1]); arena.truncate_to(saved);
1265 assert_eq!(arena.slot_count(), 5);
1266 assert_eq!(arena.len(), 5);
1267
1268 let new_id = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
1270 assert_eq!(new_id.index(), 5); assert_eq!(arena.get(new_id).unwrap().kind, NodeKind::Variable);
1272 }
1273
1274 #[test]
1275 fn test_truncate_to_preserves_retained_free_list() {
1276 let mut arena = NodeArena::new();
1277
1278 let mut ids = Vec::new();
1280 for _ in 0..8 {
1281 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1282 }
1283 assert_eq!(arena.len(), 8);
1284
1285 arena.remove(ids[2]);
1288 arena.remove(ids[6]);
1289 assert_eq!(arena.len(), 6);
1290
1291 arena.truncate_to(5);
1295
1296 assert_eq!(arena.slot_count(), 5);
1300 assert_eq!(arena.len(), 4);
1301
1302 let reused = arena.alloc(test_entry(NodeKind::Variable)).unwrap();
1305 assert_eq!(reused.index(), 2);
1306 assert_eq!(arena.get(reused).unwrap().kind, NodeKind::Variable);
1307 assert_eq!(arena.len(), 5);
1308
1309 let appended = arena.alloc(test_entry(NodeKind::Class)).unwrap();
1311 assert_eq!(appended.index(), 5);
1312 assert_eq!(arena.len(), 6);
1313 }
1314
1315 #[test]
1316 fn test_slots_read_access() {
1317 let mut arena = NodeArena::new();
1318 let placeholder = test_entry(NodeKind::Variable);
1319 arena.alloc_range(4, &placeholder).unwrap();
1320
1321 let slots = arena.slots();
1322 assert_eq!(slots.len(), 4);
1323
1324 for slot in slots {
1325 assert!(slot.is_occupied());
1326 assert_eq!(slot.generation(), 1);
1327 assert_eq!(slot.get().unwrap().kind, NodeKind::Variable);
1328 }
1329 }
1330
1331 #[test]
1332 fn test_multiple_remove_reuse_cycle() {
1333 let mut arena = NodeArena::new();
1334
1335 let mut ids = Vec::new();
1337 for _ in 0..5 {
1338 ids.push(arena.alloc(test_entry(NodeKind::Function)).unwrap());
1339 }
1340
1341 for &id in ids.iter().rev() {
1343 arena.remove(id);
1344 }
1345
1346 let new_ids: Vec<_> = (0..5)
1348 .map(|_| arena.alloc(test_entry(NodeKind::Class)).unwrap())
1349 .collect();
1350
1351 for &old_id in &ids {
1353 assert!(arena.get(old_id).is_none());
1354 }
1355
1356 for &new_id in &new_ids {
1358 assert!(arena.get(new_id).is_some());
1359 }
1360 }
1361
1362 #[test]
1363 fn test_generation_overflow_at_max_generation() {
1364 let mut arena = NodeArena::new();
1366
1367 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1369 assert_eq!(id.index(), 0);
1370
1371 arena.remove(id);
1373
1374 arena.slots[0].generation = NodeId::MAX_GENERATION;
1377
1378 let result = arena.alloc(test_entry(NodeKind::Class));
1380 assert!(result.is_err());
1381
1382 let err = result.unwrap_err();
1383 match err {
1384 ArenaError::GenerationOverflow(inner) => {
1385 assert_eq!(inner.index, 0);
1386 assert_eq!(inner.generation, NodeId::MAX_GENERATION);
1387 }
1388 _ => panic!("expected GenerationOverflow, got {err:?}"),
1389 }
1390 }
1391
1392 #[test]
1393 fn test_free_list_corruption_returns_error() {
1394 let mut arena = NodeArena::new();
1396
1397 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1399 arena.remove(id);
1400
1401 arena.slots[0].state = SlotState::Occupied(test_entry(NodeKind::Class));
1403
1404 let result = arena.alloc(test_entry(NodeKind::Method));
1406 assert!(result.is_err());
1407
1408 let err = result.unwrap_err();
1409 match err {
1410 ArenaError::FreeListCorruption { index } => {
1411 assert_eq!(index, 0);
1412 }
1413 _ => panic!("expected FreeListCorruption, got {err:?}"),
1414 }
1415
1416 assert_eq!(arena.len(), 0);
1418 }
1419
1420 #[test]
1421 fn test_arena_error_display() {
1422 let gen_err = ArenaError::GenerationOverflow(GenerationOverflowError {
1423 index: 42,
1424 generation: 100,
1425 });
1426 let display = format!("{gen_err}");
1427 assert!(display.contains("42"));
1428 assert!(display.contains("100"));
1429
1430 let corruption_err = ArenaError::FreeListCorruption { index: 5 };
1431 let display = format!("{corruption_err}");
1432 assert!(display.contains("free list corruption"));
1433 assert!(display.contains('5'));
1434
1435 let capacity_err = ArenaError::CapacityExceeded;
1436 let display = format!("{capacity_err}");
1437 assert!(display.contains("capacity exceeded"));
1438 }
1439
1440 #[test]
1441 fn test_arena_error_source_generation_overflow() {
1442 use std::error::Error;
1443
1444 let inner = GenerationOverflowError {
1445 index: 0,
1446 generation: NodeId::MAX_GENERATION,
1447 };
1448 let err = ArenaError::GenerationOverflow(inner);
1449 assert!(err.source().is_some());
1451 }
1452
1453 #[test]
1454 fn test_arena_error_source_none_for_other_variants() {
1455 use std::error::Error;
1456
1457 let corruption = ArenaError::FreeListCorruption { index: 0 };
1458 assert!(corruption.source().is_none());
1459
1460 let capacity = ArenaError::CapacityExceeded;
1461 assert!(capacity.source().is_none());
1462 }
1463
1464 #[test]
1465 fn test_arena_from_generation_overflow_error() {
1466 let inner = GenerationOverflowError {
1467 index: 10,
1468 generation: 99,
1469 };
1470 let err: ArenaError = inner.into();
1471 assert!(matches!(err, ArenaError::GenerationOverflow(_)));
1472 }
1473
1474 #[test]
1475 fn test_arena_error_clone_and_eq() {
1476 let err = ArenaError::CapacityExceeded;
1477 let cloned = err.clone();
1478 assert_eq!(err, cloned);
1479
1480 let err2 = ArenaError::FreeListCorruption { index: 3 };
1481 let cloned2 = err2.clone();
1482 assert_eq!(err2, cloned2);
1483 }
1484
1485 #[test]
1486 fn test_node_entry_with_visibility() {
1487 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file())
1488 .with_visibility(StringId::new(5));
1489
1490 assert_eq!(entry.visibility, Some(StringId::new(5)));
1491 }
1492
1493 #[test]
1494 fn test_node_entry_with_async_and_static() {
1495 let entry = NodeEntry::new(NodeKind::Method, test_name(), test_file())
1496 .with_async(true)
1497 .with_static(true);
1498
1499 assert!(entry.is_async);
1500 assert!(entry.is_static);
1501 }
1502
1503 #[test]
1504 fn test_node_entry_with_unsafe_flag() {
1505 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file()).with_unsafe(true);
1506
1507 assert!(entry.is_unsafe);
1508 }
1509
1510 #[test]
1511 fn test_node_entry_with_body_hash() {
1512 use crate::graph::body_hash::BodyHash128;
1513 let hash = BodyHash128::from_u128(0u128);
1514 let entry =
1515 NodeEntry::new(NodeKind::Function, test_name(), test_file()).with_body_hash(hash);
1516
1517 assert!(entry.body_hash.is_some());
1518 }
1519
1520 #[test]
1521 fn test_node_entry_defaults() {
1522 let entry = NodeEntry::new(NodeKind::Function, test_name(), test_file());
1523 assert_eq!(entry.start_byte, 0);
1524 assert_eq!(entry.end_byte, 0);
1525 assert_eq!(entry.start_line, 0);
1526 assert_eq!(entry.start_column, 0);
1527 assert_eq!(entry.end_line, 0);
1528 assert_eq!(entry.end_column, 0);
1529 assert!(entry.signature.is_none());
1530 assert!(entry.doc.is_none());
1531 assert!(entry.qualified_name.is_none());
1532 assert!(entry.visibility.is_none());
1533 assert!(!entry.is_async);
1534 assert!(!entry.is_static);
1535 assert!(!entry.is_unsafe);
1536 assert!(entry.body_hash.is_none());
1537 }
1538
1539 #[test]
1540 fn test_arena_default_impl() {
1541 let arena = NodeArena::default();
1542 assert_eq!(arena.len(), 0);
1543 assert!(arena.is_empty());
1544 }
1545
1546 #[test]
1547 fn test_get_invalid_id() {
1548 let mut arena = NodeArena::new();
1549 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1550 assert!(arena.get(NodeId::INVALID).is_none());
1552 }
1553
1554 #[test]
1555 fn test_get_mut_invalid_id() {
1556 let mut arena = NodeArena::new();
1557 arena.alloc(test_entry(NodeKind::Function)).unwrap();
1558 assert!(arena.get_mut(NodeId::INVALID).is_none());
1559 }
1560
1561 #[test]
1562 fn test_get_mut_wrong_generation() {
1563 let mut arena = NodeArena::new();
1564 let id = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1565 let wrong_gen_id = NodeId::new(id.index(), id.generation() + 1);
1566 assert!(arena.get_mut(wrong_gen_id).is_none());
1567 }
1568
1569 #[test]
1570 fn test_remove_invalid_id() {
1571 let mut arena = NodeArena::new();
1572 assert!(arena.remove(NodeId::INVALID).is_none());
1573 }
1574
1575 #[test]
1576 fn test_slot_get_mut_occupied() {
1577 let mut slot: Slot<i32> = Slot::new_occupied(1, 42);
1578 let val = slot.get_mut().unwrap();
1579 *val = 99;
1580 assert_eq!(slot.get(), Some(&99));
1581 }
1582
1583 #[test]
1584 fn test_slot_get_mut_vacant() {
1585 let mut slot: Slot<i32> = Slot::new_vacant(1, None);
1586 assert!(slot.get_mut().is_none());
1587 }
1588
1589 #[test]
1590 fn test_truncate_to_zero_occupied_dropped() {
1591 let mut arena = NodeArena::new();
1592 let placeholder = test_entry(NodeKind::Function);
1593 arena.alloc_range(5, &placeholder).unwrap();
1594 arena.truncate_to(0);
1596 assert_eq!(arena.len(), 0);
1597 assert_eq!(arena.slot_count(), 0);
1598 }
1599
1600 #[test]
1601 fn test_slot_count_vs_len() {
1602 let mut arena = NodeArena::new();
1603 let id0 = arena.alloc(test_entry(NodeKind::Function)).unwrap();
1604 arena.alloc(test_entry(NodeKind::Class)).unwrap();
1605 arena.remove(id0);
1606 assert_eq!(arena.slot_count(), 2);
1608 assert_eq!(arena.len(), 1);
1609 }
1610}