|
| 1 | +use std::cell::RefCell; |
| 2 | +use std::collections::HashMap; |
1 | 3 | use std::fmt; |
2 | 4 | use std::rc::Rc; |
3 | 5 |
|
4 | 6 | use crate::bytecode::Chunk; |
5 | 7 |
|
| 8 | +//============================================================================= |
| 9 | +// Symbol Interner |
| 10 | +//============================================================================= |
| 11 | +// |
| 12 | +// All symbols are interned for O(1) comparison. The interner maintains a |
| 13 | +// mapping from string content to Rc<str>, ensuring identical symbols share |
| 14 | +// the same Rc. Symbol comparison then uses Rc::ptr_eq() instead of string |
| 15 | +// comparison. |
| 16 | + |
| 17 | +thread_local! { |
| 18 | + static SYMBOL_INTERNER: RefCell<SymbolInterner> = RefCell::new(SymbolInterner::new()); |
| 19 | +} |
| 20 | + |
| 21 | +struct SymbolInterner { |
| 22 | + symbols: HashMap<Box<str>, Rc<str>>, |
| 23 | +} |
| 24 | + |
| 25 | +impl SymbolInterner { |
| 26 | + fn new() -> Self { |
| 27 | + SymbolInterner { |
| 28 | + symbols: HashMap::new(), |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + /// Intern a symbol string, returning a shared Rc<str>. |
| 33 | + /// If the symbol already exists, returns the existing Rc. |
| 34 | + /// Otherwise, creates a new Rc and stores it. |
| 35 | + fn intern(&mut self, s: &str) -> Rc<str> { |
| 36 | + if let Some(rc) = self.symbols.get(s) { |
| 37 | + return rc.clone(); |
| 38 | + } |
| 39 | + let rc: Rc<str> = Rc::from(s); |
| 40 | + self.symbols.insert(s.into(), rc.clone()); |
| 41 | + rc |
| 42 | + } |
| 43 | +} |
| 44 | + |
6 | 45 | //============================================================================= |
7 | 46 | // NaN-Boxing Implementation |
8 | 47 | //============================================================================= |
@@ -133,9 +172,10 @@ impl Value { |
133 | 172 | Value::from_heap(heap) |
134 | 173 | } |
135 | 174 |
|
136 | | - /// Create a symbol value |
| 175 | + /// Create a symbol value (interned for O(1) comparison) |
137 | 176 | pub fn symbol(s: &str) -> Value { |
138 | | - let heap = Rc::new(HeapObject::Symbol(Rc::from(s))); |
| 177 | + let interned = SYMBOL_INTERNER.with(|interner| interner.borrow_mut().intern(s)); |
| 178 | + let heap = Rc::new(HeapObject::Symbol(interned)); |
139 | 179 | Value::from_heap(heap) |
140 | 180 | } |
141 | 181 |
|
@@ -369,8 +409,11 @@ impl Value { |
369 | 409 | } |
370 | 410 |
|
371 | 411 | /// Create Symbol (backwards compat) - from Rc<str> |
| 412 | + /// Note: For proper interning, prefer Value::symbol(&str) instead |
372 | 413 | pub fn Symbol(s: Rc<str>) -> Value { |
373 | | - let heap = Rc::new(HeapObject::Symbol(s)); |
| 414 | + // Re-intern to ensure pointer equality works correctly |
| 415 | + let interned = SYMBOL_INTERNER.with(|interner| interner.borrow_mut().intern(&s)); |
| 416 | + let heap = Rc::new(HeapObject::Symbol(interned)); |
374 | 417 | Value::from_heap(heap) |
375 | 418 | } |
376 | 419 |
|
@@ -549,7 +592,8 @@ impl PartialEq for Value { |
549 | 592 | // Heap object comparison |
550 | 593 | match (self.as_heap(), other.as_heap()) { |
551 | 594 | (Some(HeapObject::String(a)), Some(HeapObject::String(b))) => a == b, |
552 | | - (Some(HeapObject::Symbol(a)), Some(HeapObject::Symbol(b))) => a == b, |
| 595 | + // Symbol comparison uses Rc::ptr_eq for O(1) - symbols are interned! |
| 596 | + (Some(HeapObject::Symbol(a)), Some(HeapObject::Symbol(b))) => Rc::ptr_eq(a, b), |
553 | 597 | (Some(HeapObject::List(a)), Some(HeapObject::List(b))) => a == b, |
554 | 598 | (Some(HeapObject::Cons(a)), Some(HeapObject::Cons(b))) => { |
555 | 599 | a.car == b.car && a.cdr == b.cdr |
|
0 commit comments