use flash_text_size::TextSize;
use hashbrown::hash_map::{RawEntryMut, RawOccupiedEntryMut, RawVacantEntryMut};
use rustc_hash::FxHasher;
use std::fmt::{self, Debug, Formatter};
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Not;
use std::ptr::NonNull;
use crate::green::Slot;
use crate::syntax::{TriviaPiece, TriviaPieceKind};
use crate::{
GreenNode, GreenNodeData, GreenToken, GreenTokenData, NodeOrToken, RawSyntaxKind,
green::GreenElementRef,
};
use super::element::GreenElement;
use super::trivia::{GreenTrivia, GreenTriviaData};
type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
struct GenerationalPointer<T: IntoRawPointer> {
data: usize,
_ty: PhantomData<T>,
}
impl<T: IntoRawPointer> GenerationalPointer<T> {
fn new(value: T, generation: Generation) -> Self {
let ptr = value.into_raw();
let mut data = ptr as usize;
debug_assert!(data & 1 == 0);
data |= generation as usize;
Self {
data,
_ty: PhantomData,
}
}
fn value(&self) -> &T::Pointee {
let data = self.data & !1;
let ptr = data as *const T::Pointee;
unsafe { &*ptr }
}
fn generation(&self) -> Generation {
match self.data & 1 {
0 => Generation::A,
1 => Generation::B,
_ => unreachable!(),
}
}
fn set_generation(&mut self, generation: Generation) {
let data = self.data & !1;
self.data = data | generation as usize;
}
}
impl<T: IntoRawPointer> Debug for GenerationalPointer<T>
where
T::Pointee: Debug,
{
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
fmt.debug_struct("GenerationalPointer")
.field("value", self.value())
.field("generation", &self.generation())
.finish()
}
}
impl<T: IntoRawPointer> Drop for GenerationalPointer<T> {
fn drop(&mut self) {
let ptr = self.value() as *const _ as *mut _;
let value = unsafe { T::from_raw(ptr) };
drop(value);
}
}
trait IntoRawPointer {
type Pointee;
fn into_raw(self) -> *mut Self::Pointee;
unsafe fn from_raw(ptr: *mut Self::Pointee) -> Self;
}
#[derive(Debug)]
struct CachedToken(GenerationalPointer<GreenToken>);
impl IntoRawPointer for GreenToken {
type Pointee = GreenTokenData;
fn into_raw(self) -> *mut Self::Pointee {
Self::into_raw(self).as_ptr()
}
unsafe fn from_raw(ptr: *mut Self::Pointee) -> Self {
unsafe { Self::from_raw(NonNull::new(ptr).unwrap()) }
}
}
#[derive(Debug)]
struct CachedNode {
node: GenerationalPointer<GreenNode>,
hash: u64,
}
impl IntoRawPointer for GreenNode {
type Pointee = GreenNodeData;
fn into_raw(self) -> *mut Self::Pointee {
Self::into_raw(self).as_ptr()
}
unsafe fn from_raw(ptr: *mut Self::Pointee) -> Self {
unsafe { Self::from_raw(NonNull::new(ptr).unwrap()) }
}
}
#[derive(Default, Debug)]
pub struct NodeCache {
nodes: HashMap<CachedNode, ()>,
tokens: HashMap<CachedToken, ()>,
trivia: TriviaCache,
generation: Generation,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
enum Generation {
#[default]
A = 0,
B = 1,
}
impl Not for Generation {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Self::A => Self::B,
Self::B => Self::A,
}
}
}
fn token_hash_of(kind: RawSyntaxKind, text: &str) -> u64 {
let mut h = FxHasher::default();
kind.hash(&mut h);
text.hash(&mut h);
h.finish()
}
fn token_hash(token: &GreenTokenData) -> u64 {
token_hash_of(token.kind(), token.text())
}
fn element_id(elem: GreenElementRef<'_>) -> *const () {
match elem {
NodeOrToken::Node(it) => it as *const GreenNodeData as *const (),
NodeOrToken::Token(it) => it as *const GreenTokenData as *const (),
}
}
impl NodeCache {
const UNCACHED_NODE_HASH: u64 = 0;
pub(crate) fn node(
&mut self,
kind: RawSyntaxKind,
children: &[(u64, GreenElement)],
) -> NodeCacheNodeEntryMut<'_> {
if children.len() > 3 {
return NodeCacheNodeEntryMut::NoCache(Self::UNCACHED_NODE_HASH);
}
let hash = {
let mut h = FxHasher::default();
kind.hash(&mut h);
for &(hash, _) in children {
if hash == Self::UNCACHED_NODE_HASH {
return NodeCacheNodeEntryMut::NoCache(Self::UNCACHED_NODE_HASH);
}
hash.hash(&mut h);
}
h.finish()
};
let entry = self.nodes.raw_entry_mut().from_hash(hash, |no_hash| {
no_hash.node.value().kind() == kind && {
let lhs = no_hash.node.value().slots().filter_map(|slot| match slot {
Slot::Empty { .. } => None,
Slot::Node { node, .. } => Some(element_id(NodeOrToken::Node(node))),
Slot::Token { token, .. } => Some(element_id(NodeOrToken::Token(token))),
});
let rhs = children
.iter()
.map(|(_, element)| element_id(element.as_deref()));
lhs.eq(rhs)
}
});
match entry {
RawEntryMut::Occupied(mut entry) => {
entry.key_mut().node.set_generation(self.generation);
NodeCacheNodeEntryMut::Cached(CachedNodeEntry {
hash,
raw_entry: entry,
})
}
RawEntryMut::Vacant(entry) => NodeCacheNodeEntryMut::Vacant(VacantNodeEntry {
raw_entry: entry,
original_kind: kind,
hash,
generation: self.generation,
}),
}
}
pub(crate) fn token(&mut self, kind: RawSyntaxKind, text: &str) -> (u64, GreenToken) {
self.token_with_trivia(kind, text, &[], &[])
}
pub(crate) fn token_with_trivia(
&mut self,
kind: RawSyntaxKind,
text: &str,
leading: &[TriviaPiece],
trailing: &[TriviaPiece],
) -> (u64, GreenToken) {
let hash = token_hash_of(kind, text);
let entry = self.tokens.raw_entry_mut().from_hash(hash, |token| {
token.0.value().kind() == kind && token.0.value().text() == text
});
let token = match entry {
RawEntryMut::Occupied(mut entry) => {
entry.key_mut().0.set_generation(self.generation);
entry.key().0.value().to_owned()
}
RawEntryMut::Vacant(entry) => {
let leading = self.trivia.get(self.generation, leading);
let trailing = self.trivia.get(self.generation, trailing);
let token = GreenToken::with_trivia(kind, text, leading, trailing);
let key = CachedToken(GenerationalPointer::new(token.clone(), self.generation));
entry.insert_with_hasher(hash, key, (), |t| token_hash(t.0.value()));
token
}
};
(hash, token)
}
pub(crate) fn increment_generation(&mut self) {
debug_assert!(
self.nodes
.keys()
.all(|entry| entry.node.generation() == self.generation)
&& self
.tokens
.keys()
.all(|token| token.0.generation() == self.generation)
&& self
.trivia
.cache
.keys()
.all(|trivia| trivia.0.generation() == self.generation)
);
self.generation = !self.generation;
}
pub(crate) fn retain_cache(&mut self) {
self.nodes
.retain(|node, _| node.node.generation() == self.generation);
self.tokens
.retain(|token, _| token.0.generation() == self.generation);
self.trivia
.cache
.retain(|trivia, _| trivia.0.generation() == self.generation);
}
}
pub(crate) enum NodeCacheNodeEntryMut<'a> {
Cached(CachedNodeEntry<'a>),
NoCache(u64),
Vacant(VacantNodeEntry<'a>),
}
pub(crate) struct VacantNodeEntry<'a> {
hash: u64,
original_kind: RawSyntaxKind,
raw_entry: RawVacantEntryMut<'a, CachedNode, (), BuildHasherDefault<FxHasher>>,
generation: Generation,
}
pub(crate) struct CachedNodeEntry<'a> {
hash: u64,
raw_entry: RawOccupiedEntryMut<'a, CachedNode, (), BuildHasherDefault<FxHasher>>,
}
impl CachedNodeEntry<'_> {
pub fn node(&self) -> &GreenNodeData {
self.raw_entry.key().node.value()
}
pub fn hash(&self) -> u64 {
self.hash
}
}
impl VacantNodeEntry<'_> {
pub fn cache(self, node: GreenNode) -> u64 {
if self.original_kind != node.kind() {
NodeCache::UNCACHED_NODE_HASH
} else {
self.raw_entry.insert_with_hasher(
self.hash,
CachedNode {
node: GenerationalPointer::new(node, self.generation),
hash: self.hash,
},
(),
|n| n.hash,
);
self.hash
}
}
}
#[derive(Debug)]
struct CachedTrivia(GenerationalPointer<GreenTrivia>);
impl IntoRawPointer for GreenTrivia {
type Pointee = GreenTriviaData;
fn into_raw(self) -> *mut Self::Pointee {
Self::into_raw(self)
}
unsafe fn from_raw(ptr: *mut Self::Pointee) -> Self {
unsafe { Self::from_raw(ptr) }
}
}
#[derive(Debug)]
struct TriviaCache {
cache: HashMap<CachedTrivia, ()>,
whitespace: GreenTrivia,
}
impl Default for TriviaCache {
fn default() -> Self {
Self {
cache: Default::default(),
whitespace: GreenTrivia::new([TriviaPiece::whitespace(1)]),
}
}
}
impl TriviaCache {
fn get(&mut self, generation: Generation, pieces: &[TriviaPiece]) -> GreenTrivia {
match pieces {
[] => GreenTrivia::empty(),
[
TriviaPiece {
kind: TriviaPieceKind::Whitespace,
length,
},
] if *length == TextSize::from(1) => self.whitespace.clone(),
_ => {
let hash = Self::trivia_hash_of(pieces);
let entry = self
.cache
.raw_entry_mut()
.from_hash(hash, |trivia| trivia.0.value().pieces() == pieces);
match entry {
RawEntryMut::Occupied(mut entry) => {
entry.key_mut().0.set_generation(generation);
entry.key().0.value().to_owned()
}
RawEntryMut::Vacant(entry) => {
let trivia = GreenTrivia::new(pieces.iter().copied());
entry.insert_with_hasher(
hash,
CachedTrivia(GenerationalPointer::new(trivia.clone(), generation)),
(),
|cached| Self::trivia_hash_of(cached.0.value().pieces()),
);
trivia
}
}
}
}
}
fn trivia_hash_of(pieces: &[TriviaPiece]) -> u64 {
let mut h = FxHasher::default();
pieces.len().hash(&mut h);
for piece in pieces {
piece.hash(&mut h);
}
h.finish()
}
}
#[cfg(test)]
mod tests {
use std::mem::size_of;
use crate::green::node_cache::{CachedNode, CachedToken, CachedTrivia, token_hash};
use crate::green::trivia::GreenTrivia;
use crate::{GreenToken, RawSyntaxKind};
use flash_text_size::TextSize;
#[test]
fn green_token_hash() {
let kind = RawSyntaxKind(0);
let text = " let ";
let t1 = GreenToken::with_trivia(
kind,
text,
GreenTrivia::whitespace(TextSize::from(1)),
GreenTrivia::whitespace(TextSize::from(1)),
);
let t2 = GreenToken::with_trivia(
kind,
text,
GreenTrivia::whitespace(1),
GreenTrivia::whitespace(1),
);
assert_eq!(token_hash(&t1), token_hash(&t2));
let t3 = GreenToken::new_raw(kind, "let");
assert_ne!(token_hash(&t1), token_hash(&t3));
let t4 = GreenToken::with_trivia(
kind,
"\tlet ",
GreenTrivia::whitespace(1),
GreenTrivia::whitespace(1),
);
assert_ne!(token_hash(&t1), token_hash(&t4));
}
#[test]
fn cache_entry_size() {
assert_eq!(size_of::<CachedNode>(), 16);
assert_eq!(size_of::<CachedToken>(), 8);
assert_eq!(size_of::<CachedTrivia>(), 8);
}
}