1
Fork 0
mirror of https://github.com/RGBCube/cstree synced 2025-07-27 09:07:44 +00:00

Merge pull request #25 from domenicquirl/v0.6

This commit is contained in:
DQ 2021-06-23 14:31:08 +02:00 committed by GitHub
commit 1728df28f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 157 additions and 56 deletions

View file

@ -1,33 +1,44 @@
[package] [package]
edition = "2018" edition = "2018"
name = "cstree" name = "cstree"
version = "0.5.0" version = "0.6.0"
authors = ["Domenic Quirl <DomenicQuirl@pm.me>", "Aleksey Kladov <aleksey.kladov@gmail.com>"] authors = [
"Domenic Quirl <DomenicQuirl@pm.me>",
"Aleksey Kladov <aleksey.kladov@gmail.com>",
]
description = "Library for generic lossless syntax trees" description = "Library for generic lossless syntax trees"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/domenicquirl/cstree" repository = "https://github.com/domenicquirl/cstree"
readme = "README.md" readme = "README.md"
[profile.release]
debug = true
[dependencies] [dependencies]
lasso = "0.5" lasso = { version="0.5", features=["inline-more"] }
text-size = "1.0.0" text-size = "1.0.0"
fxhash= "0.2.1" fxhash = "0.2.1"
parking_lot= "0.11.1" parking_lot = "0.11.1"
# Arc # Arc
triomphe = "0.1.2" triomphe = "0.1.2"
[dependencies.serde] [dependencies.serde]
version = "1.0" version = "1.0"
optional = true optional = true
default-features = false default-features = false
features = ["derive"] features = ["derive"]
[dev-dependencies] [dev-dependencies]
m_lexer = "0.0.4" m_lexer = "0.0.4"
serde_json = "1.0.61" serde_json = "1.0.61"
serde_test = "1.0.119" serde_test = "1.0.119"
crossbeam-utils = "0.8" crossbeam-utils = "0.8"
criterion = "0.3"
[[bench]]
name = "main"
harness = false
[features] [features]
default = [] default = []

84
benches/main.rs Normal file
View file

@ -0,0 +1,84 @@
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use cstree::*;
use lasso::{Interner, Rodeo};
#[derive(Debug)]
pub enum Element<'s> {
Node(Vec<Element<'s>>),
Token(&'s str),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TestLang {}
impl Language for TestLang {
type Kind = SyntaxKind;
fn kind_from_raw(raw: SyntaxKind) -> Self::Kind {
raw
}
fn kind_to_raw(kind: Self::Kind) -> SyntaxKind {
kind
}
}
pub fn build_tree_with_cache<'c, 'i, I>(root: &Element<'_>, cache: &'c mut NodeCache<'i, I>) -> GreenNode
where
I: Interner,
{
let mut builder = GreenNodeBuilder::with_cache(cache);
build_recursive(root, &mut builder, 0);
let (node, interner) = builder.finish();
assert!(interner.is_none());
node
}
pub fn build_recursive<'c, 'i, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'c, 'i, I>, mut from: u16) -> u16
where
I: Interner,
{
match root {
Element::Node(children) => {
builder.start_node(SyntaxKind(from));
for child in children {
from = build_recursive(child, builder, from + 1);
}
builder.finish_node();
}
Element::Token(text) => {
builder.token(SyntaxKind(from), *text);
}
}
from
}
fn two_level_tree() -> Element<'static> {
use Element::*;
Node(vec![
Node(vec![Token("0.0"), Token("0.1")]),
Node(vec![Token("1.0")]),
Node(vec![Token("2.0"), Token("2.1"), Token("2.2")]),
])
}
pub fn create(c: &mut Criterion) {
let mut group = c.benchmark_group("qualification");
group.throughput(Throughput::Elements(1));
let mut interner = Rodeo::new();
let mut cache = NodeCache::with_interner(&mut interner);
let tree = two_level_tree();
group.bench_function("two-level tree", |b| {
b.iter(|| {
for _ in 0..100_000 {
let _tree = build_tree_with_cache(&tree, &mut cache);
}
})
});
group.finish();
}
criterion_group!(benches, create);
criterion_main!(benches);

View file

@ -1,6 +1,9 @@
use std::convert::TryFrom; use std::{
convert::TryFrom,
hash::{Hash, Hasher},
};
use fxhash::FxHashMap; use fxhash::{FxHashMap, FxHasher32};
use text_size::TextSize; use text_size::TextSize;
use crate::{ use crate::{
@ -132,11 +135,11 @@ where
} }
impl ChildrenIter { impl ChildrenIter {
fn new(data: [Option<GreenElement>; CHILDREN_CACHE_THRESHOLD], count: usize) -> Self { fn new() -> Self {
ChildrenIter { ChildrenIter {
data, data: [None, None, None],
idx: 0, idx: 0,
len: count, len: 0,
} }
} }
} }
@ -157,19 +160,24 @@ where
} }
} }
let mut data: [Option<GreenElement>; CHILDREN_CACHE_THRESHOLD] = [None, None, None]; let mut new_children = ChildrenIter::new();
let mut count = 0; let mut hasher = FxHasher32::default();
let mut text_len: TextSize = 0.into();
for child in children { for (i, child) in children.into_iter().enumerate() {
data[count] = Some(child); text_len += child.text_len();
count += 1; child.hash(&mut hasher);
new_children.data[i] = Some(child);
new_children.len += 1;
} }
let children = ChildrenIter::new(data, count);
let head = GreenNodeHead::from_child_iter(kind, children.clone()); let head = GreenNodeHead {
kind,
text_len,
child_hash: hasher.finish() as u32,
};
self.nodes self.nodes
.entry(head.clone()) .entry(head)
.or_insert_with(|| GreenNode::from_head_and_children(head, children)) .or_insert_with_key(|head| GreenNode::from_head_and_children(head.clone(), new_children))
.clone() .clone()
} }
@ -177,7 +185,10 @@ where
let text_len = TextSize::try_from(text.len()).unwrap(); let text_len = TextSize::try_from(text.len()).unwrap();
let text = self.interner.get_or_intern(text); let text = self.interner.get_or_intern(text);
let data = GreenTokenData { kind, text, text_len }; let data = GreenTokenData { kind, text, text_len };
self.tokens.entry(data).or_insert_with(|| GreenToken::new(data)).clone() self.tokens
.entry(data)
.or_insert_with_key(|data| GreenToken::new(*data))
.clone()
} }
} }

View file

@ -15,29 +15,9 @@ use triomphe::{Arc, HeaderWithLength, ThinArc};
#[repr(align(2))] //to use 1 bit for pointer tagging. NB: this is an at-least annotation #[repr(align(2))] //to use 1 bit for pointer tagging. NB: this is an at-least annotation
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(super) struct GreenNodeHead { pub(super) struct GreenNodeHead {
kind: SyntaxKind, pub(super) kind: SyntaxKind,
text_len: TextSize, pub(super) text_len: TextSize,
child_hash: u32, pub(super) child_hash: u32,
}
impl GreenNodeHead {
#[inline]
pub(super) fn from_child_iter<I>(kind: SyntaxKind, children: I) -> Self
where
I: Iterator<Item = GreenElement>,
{
let mut hasher = FxHasher32::default();
let mut text_len: TextSize = 0.into();
for child in children {
text_len += child.text_len();
child.hash(&mut hasher);
}
Self {
kind,
text_len,
child_hash: hasher.finish() as u32,
}
}
} }
/// Internal node in the immutable "green" tree. /// Internal node in the immutable "green" tree.

View file

@ -22,7 +22,6 @@ use crate::{
/// # See also /// # See also
/// [`SyntaxNode`] /// [`SyntaxNode`]
/// [`SyntaxNode::new_root_with_resolver`] /// [`SyntaxNode::new_root_with_resolver`]
#[derive(Clone)]
#[repr(transparent)] #[repr(transparent)]
pub struct ResolvedNode<L: Language, D: 'static = ()> { pub struct ResolvedNode<L: Language, D: 'static = ()> {
pub(super) syntax: SyntaxNode<L, D>, pub(super) syntax: SyntaxNode<L, D>,
@ -41,6 +40,14 @@ impl<L: Language, D> ResolvedNode<L, D> {
} }
} }
impl<L: Language, D> Clone for ResolvedNode<L, D> {
fn clone(&self) -> Self {
Self {
syntax: self.syntax.clone(),
}
}
}
impl<L: Language, D> Deref for ResolvedNode<L, D> { impl<L: Language, D> Deref for ResolvedNode<L, D> {
type Target = SyntaxNode<L, D>; type Target = SyntaxNode<L, D>;
@ -77,6 +84,14 @@ impl<L: Language, D> ResolvedToken<L, D> {
} }
} }
impl<L: Language, D> Clone for ResolvedToken<L, D> {
fn clone(&self) -> Self {
Self {
syntax: self.syntax.clone(),
}
}
}
impl<L: Language, D> Deref for ResolvedToken<L, D> { impl<L: Language, D> Deref for ResolvedToken<L, D> {
type Target = SyntaxToken<L, D>; type Target = SyntaxToken<L, D>;