mirror of
https://github.com/RGBCube/cstree
synced 2025-07-27 17:17:45 +00:00
Merge pull request #25 from domenicquirl/v0.6
This commit is contained in:
commit
1728df28f8
5 changed files with 157 additions and 56 deletions
17
Cargo.toml
17
Cargo.toml
|
@ -1,15 +1,21 @@
|
||||||
[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"
|
||||||
|
@ -28,6 +34,11 @@ 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
84
benches/main.rs
Normal 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);
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue