use std::fmt::Display;
use std::ops::Index;
use std::ops::IndexMut;
use controlled_option::ControlledOption;
use either::Either;
use fxhash::FxHashMap;
use smallvec::SmallVec;
use crate::arena::Arena;
use crate::arena::Handle;
use crate::arena::SupplementalArena;
#[repr(C)]
struct InternedString {
start: *const u8,
len: usize,
}
const INITIAL_STRING_CAPACITY: usize = 512;
struct InternedStringContent {
current_buffer: Vec<u8>,
full_buffers: Vec<Vec<u8>>,
}
impl InternedStringContent {
fn new() -> InternedStringContent {
InternedStringContent {
current_buffer: Vec::with_capacity(INITIAL_STRING_CAPACITY),
full_buffers: Vec::new(),
}
}
fn add(&mut self, value: &str) -> InternedString {
let value = value.as_bytes();
let len = value.len();
let capacity = self.current_buffer.capacity();
let remaining_capacity = capacity - self.current_buffer.len();
if len > remaining_capacity {
let new_capacity = (capacity.max(len) + 1).next_power_of_two();
let new_buffer = Vec::with_capacity(new_capacity);
let old_buffer = std::mem::replace(&mut self.current_buffer, new_buffer);
self.full_buffers.push(old_buffer);
}
let start_index = self.current_buffer.len();
self.current_buffer.extend_from_slice(value);
let start = unsafe { self.current_buffer.as_ptr().add(start_index) };
InternedString { start, len }
}
}
impl InternedString {
fn as_str(&self) -> &str {
unsafe {
let bytes = std::slice::from_raw_parts(self.start, self.len);
std::str::from_utf8_unchecked(bytes)
}
}
unsafe fn as_hash_key(&self) -> &'static str {
let bytes = std::slice::from_raw_parts(self.start, self.len);
std::str::from_utf8_unchecked(bytes)
}
}
#[repr(C)]
pub struct Symbol {
content: InternedString,
}
impl Symbol {
fn as_str(&self) -> &str {
self.content.as_str()
}
}
impl PartialEq<&str> for Symbol {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl StackGraph {
pub fn add_symbol<S: AsRef<str> + ?Sized>(&mut self, symbol: &S) -> Handle<Symbol> {
let symbol = symbol.as_ref();
if let Some(handle) = self.symbol_handles.get(symbol) {
return *handle;
}
let interned = self.interned_strings.add(symbol);
let hash_key = unsafe { interned.as_hash_key() };
let handle = self.symbols.add(Symbol { content: interned });
self.symbol_handles.insert(hash_key, handle);
handle
}
pub fn iter_symbols(&self) -> impl Iterator<Item = Handle<Symbol>> {
self.symbols.iter_handles()
}
}
impl Index<Handle<Symbol>> for StackGraph {
type Output = str;
#[inline(always)]
fn index(&self, handle: Handle<Symbol>) -> &str {
self.symbols.get(handle).as_str()
}
}
#[doc(hidden)]
pub struct DisplaySymbol<'a> {
wrapped: Handle<Symbol>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplaySymbol<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.graph[self.wrapped])
}
}
impl Handle<Symbol> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplaySymbol {
wrapped: self,
graph,
}
}
}
pub struct File {
name: InternedString,
}
impl File {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl StackGraph {
pub fn add_file<S: AsRef<str> + ?Sized>(
&mut self,
name: &S,
) -> Result<Handle<File>, Handle<File>> {
let name = name.as_ref();
if let Some(handle) = self.file_handles.get(name) {
return Err(*handle);
}
let interned = self.interned_strings.add(name);
let hash_key = unsafe { interned.as_hash_key() };
let handle = self.files.add(File { name: interned });
self.file_handles.insert(hash_key, handle);
Ok(handle)
}
#[inline(always)]
pub fn get_or_create_file<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> Handle<File> {
self.add_file(name).unwrap_or_else(|handle| handle)
}
pub fn get_file_unchecked<S: AsRef<str> + ?Sized>(&self, name: &S) -> Handle<File> {
let name = name.as_ref();
self.file_handles.get(name).copied().expect("Missing file")
}
}
impl StackGraph {
pub fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
self.node_id_handles.nodes_for_file(file)
}
pub fn iter_files(&self) -> impl Iterator<Item = Handle<File>> + '_ {
self.files.iter_handles()
}
}
impl Display for File {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
impl Index<Handle<File>> for StackGraph {
type Output = File;
#[inline(always)]
fn index(&self, handle: Handle<File>) -> &File {
&self.files.get(handle)
}
}
#[doc(hidden)]
pub struct DisplayFile<'a> {
wrapped: Handle<File>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.graph[self.wrapped])
}
}
impl Handle<File> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayFile {
wrapped: self,
graph,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NodeID {
file: ControlledOption<Handle<File>>,
local_id: u32,
}
const ROOT_NODE_ID: u32 = 1;
const JUMP_TO_NODE_ID: u32 = 2;
impl NodeID {
#[inline(always)]
pub fn root() -> NodeID {
NodeID {
file: ControlledOption::none(),
local_id: ROOT_NODE_ID,
}
}
#[inline(always)]
pub fn jump_to() -> NodeID {
NodeID {
file: ControlledOption::none(),
local_id: JUMP_TO_NODE_ID,
}
}
#[inline(always)]
pub fn new_in_file(file: Handle<File>, local_id: u32) -> NodeID {
NodeID {
file: ControlledOption::some(file),
local_id,
}
}
#[inline(always)]
pub fn is_root(self) -> bool {
self.file.is_none() && self.local_id == ROOT_NODE_ID
}
#[inline(always)]
pub fn is_jump_to(self) -> bool {
self.file.is_none() && self.local_id == JUMP_TO_NODE_ID
}
#[inline(always)]
pub fn file(self) -> Option<Handle<File>> {
self.file.into()
}
#[inline(always)]
pub fn local_id(self) -> u32 {
debug_assert!(self.file.is_some());
self.local_id
}
#[inline(always)]
pub fn is_in_file(self, file: Handle<File>) -> bool {
match self.file.into_option() {
Some(this_file) => this_file == file,
_ => true,
}
}
}
#[doc(hidden)]
pub struct DisplayNodeID<'a> {
wrapped: NodeID,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNodeID<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.wrapped.file.into_option() {
Some(file) => write!(f, "{}({})", file.display(self.graph), self.wrapped.local_id),
None => {
if self.wrapped.is_root() {
write!(f, "[root]")
} else if self.wrapped.is_jump_to() {
write!(f, "[jump]")
} else {
unreachable!();
}
}
}
}
}
impl NodeID {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNodeID {
wrapped: self,
graph,
}
}
}
#[repr(C)]
pub enum Node {
DropScopes(DropScopesNode),
ExportedScope(ExportedScopeNode),
InternalScope(InternalScopeNode),
JumpTo(JumpToNode),
PopScopedSymbol(PopScopedSymbolNode),
PopSymbol(PopSymbolNode),
PushScopedSymbol(PushScopedSymbolNode),
PushSymbol(PushSymbolNode),
Root(RootNode),
}
impl Node {
#[inline(always)]
pub fn is_definition(&self) -> bool {
match self {
Node::PopScopedSymbol(node) => node.is_definition,
Node::PopSymbol(node) => node.is_definition,
_ => false,
}
}
#[inline(always)]
pub fn is_reference(&self) -> bool {
match self {
Node::PushScopedSymbol(node) => node.is_reference,
Node::PushSymbol(node) => node.is_reference,
_ => false,
}
}
#[inline(always)]
pub fn is_jump_to(&self) -> bool {
matches!(self, Node::JumpTo(_))
}
#[inline(always)]
pub fn is_root(&self) -> bool {
matches!(self, Node::Root(_))
}
pub fn symbol(&self) -> Option<Handle<Symbol>> {
match self {
Node::PushScopedSymbol(node) => Some(node.symbol),
Node::PushSymbol(node) => Some(node.symbol),
Node::PopScopedSymbol(node) => Some(node.symbol),
Node::PopSymbol(node) => Some(node.symbol),
_ => None,
}
}
pub fn scope(&self) -> Option<NodeID> {
match self {
Node::PushScopedSymbol(node) => Some(node.scope),
_ => None,
}
}
pub fn id(&self) -> NodeID {
match self {
Node::DropScopes(node) => node.id,
Node::ExportedScope(node) => node.id,
Node::InternalScope(node) => node.id,
Node::JumpTo(node) => node.id,
Node::PushScopedSymbol(node) => node.id,
Node::PushSymbol(node) => node.id,
Node::PopScopedSymbol(node) => node.id,
Node::PopSymbol(node) => node.id,
Node::Root(node) => node.id,
}
}
#[inline(always)]
pub fn file(&self) -> Option<Handle<File>> {
self.id().file()
}
#[inline(always)]
pub fn is_in_file(&self, file: Handle<File>) -> bool {
self.id().is_in_file(file)
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayNode {
wrapped: self,
graph,
}
}
}
impl StackGraph {
#[inline(always)]
pub fn jump_to_node(&self) -> Handle<Node> {
self.jump_to_node
}
#[inline(always)]
pub fn root_node(&self) -> Handle<Node> {
self.root_node
}
pub fn new_node_id(&mut self, file: Handle<File>) -> NodeID {
self.node_id_handles.unused_id(file)
}
pub fn iter_nodes(&self) -> impl Iterator<Item = Handle<Node>> {
self.nodes.iter_handles()
}
pub fn node_for_id(&self, id: NodeID) -> Option<Handle<Node>> {
if id.file().is_some() {
self.node_id_handles.try_handle_for_id(id)
} else if id.is_root() {
Some(self.root_node())
} else if id.is_jump_to() {
Some(self.jump_to_node())
} else {
None
}
}
pub(crate) fn add_node(&mut self, id: NodeID, node: Node) -> Option<Handle<Node>> {
if let Some(_) = self.node_id_handles.handle_for_id(id) {
return None;
}
let handle = self.nodes.add(node);
self.node_id_handles.set_handle_for_id(id, handle);
Some(handle)
}
pub(crate) fn get_or_create_node(&mut self, id: NodeID, node: Node) -> Handle<Node> {
if let Some(handle) = self.node_id_handles.handle_for_id(id) {
return handle;
}
let handle = self.nodes.add(node);
self.node_id_handles.set_handle_for_id(id, handle);
handle
}
}
#[doc(hidden)]
pub struct DisplayNode<'a> {
wrapped: &'a Node,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.wrapped {
Node::DropScopes(node) => node.display(self.graph).fmt(f),
Node::ExportedScope(node) => node.display(self.graph).fmt(f),
Node::InternalScope(node) => node.display(self.graph).fmt(f),
Node::JumpTo(node) => node.fmt(f),
Node::PushScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PushSymbol(node) => node.display(self.graph).fmt(f),
Node::PopScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PopSymbol(node) => node.display(self.graph).fmt(f),
Node::Root(node) => node.fmt(f),
}
}
}
impl Handle<Node> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNode {
wrapped: &graph[self],
graph,
}
}
}
impl Index<Handle<Node>> for StackGraph {
type Output = Node;
#[inline(always)]
fn index(&self, handle: Handle<Node>) -> &Node {
self.nodes.get(handle)
}
}
impl IndexMut<Handle<Node>> for StackGraph {
#[inline(always)]
fn index_mut(&mut self, handle: Handle<Node>) -> &mut Node {
self.nodes.get_mut(handle)
}
}
#[repr(C)]
pub struct DropScopesNode {
pub id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_clickable: bool,
}
impl From<DropScopesNode> for Node {
fn from(node: DropScopesNode) -> Node {
Node::DropScopes(node)
}
}
impl StackGraph {
pub fn add_drop_scopes_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
let node = DropScopesNode {
id,
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_clickable: false,
};
self.add_node(id, node.into())
}
}
impl DropScopesNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayDropScopesNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayDropScopesNode<'a> {
wrapped: &'a DropScopesNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayDropScopesNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(f, "[{} drop scopes]", self.wrapped.id.display(self.graph))
}
}
}
#[repr(C)]
pub struct ExportedScopeNode {
pub id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_clickable: bool,
}
impl From<ExportedScopeNode> for Node {
fn from(node: ExportedScopeNode) -> Node {
Node::ExportedScope(node)
}
}
impl StackGraph {
pub fn add_exported_scope_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
let node = ExportedScopeNode {
id,
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_clickable: false,
};
self.add_node(id, node.into())
}
}
impl ExportedScopeNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayExportedScopeNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayExportedScopeNode<'a> {
wrapped: &'a ExportedScopeNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayExportedScopeNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} exported scope]",
self.wrapped.id.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct InternalScopeNode {
pub id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_clickable: bool,
}
impl From<InternalScopeNode> for Node {
fn from(node: InternalScopeNode) -> Node {
Node::InternalScope(node)
}
}
impl StackGraph {
pub fn add_internal_scope_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
let node = InternalScopeNode {
id,
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_clickable: false,
};
self.add_node(id, node.into())
}
}
impl InternalScopeNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayInternalScopeNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayInternalScopeNode<'a> {
wrapped: &'a InternalScopeNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayInternalScopeNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} internal scope]",
self.wrapped.id.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct JumpToNode {
id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_clickable: bool,
}
impl From<JumpToNode> for Node {
fn from(node: JumpToNode) -> Node {
Node::JumpTo(node)
}
}
impl JumpToNode {
fn new() -> JumpToNode {
JumpToNode {
id: NodeID::jump_to(),
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_clickable: false,
}
}
}
impl Display for JumpToNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[jump to scope]")
}
}
#[repr(C)]
pub struct PopScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_definition: bool,
}
impl From<PopScopedSymbolNode> for Node {
fn from(node: PopScopedSymbolNode) -> Node {
Node::PopScopedSymbol(node)
}
}
impl StackGraph {
pub fn add_pop_scoped_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_definition: bool,
) -> Option<Handle<Node>> {
let node = PopScopedSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_definition,
};
self.add_node(id, node.into())
}
}
impl PopScopedSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopScopedSymbolNode<'a> {
wrapped: &'a PopScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"scoped definition"
} else {
"pop scoped"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PopSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_definition: bool,
}
impl From<PopSymbolNode> for Node {
fn from(node: PopSymbolNode) -> Node {
Node::PopSymbol(node)
}
}
impl StackGraph {
pub fn add_pop_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_definition: bool,
) -> Option<Handle<Node>> {
let node = PopSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_definition,
};
self.add_node(id, node.into())
}
}
impl PopSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopSymbolNode<'a> {
wrapped: &'a PopSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"definition"
} else {
"pop"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PushScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub scope: NodeID,
pub is_reference: bool,
_phantom: (),
}
impl From<PushScopedSymbolNode> for Node {
fn from(node: PushScopedSymbolNode) -> Node {
Node::PushScopedSymbol(node)
}
}
impl StackGraph {
pub fn add_push_scoped_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
scope: NodeID,
is_reference: bool,
) -> Option<Handle<Node>> {
let node = PushScopedSymbolNode {
id,
symbol,
scope,
is_reference,
_phantom: (),
};
self.add_node(id, node.into())
}
}
impl PushScopedSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushScopedSymbolNode<'a> {
wrapped: &'a PushScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"scoped reference"
} else {
"push scoped"
},
self.wrapped.symbol.display(self.graph),
self.wrapped.scope.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct PushSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
_scope: NodeID,
pub is_reference: bool,
}
impl From<PushSymbolNode> for Node {
fn from(node: PushSymbolNode) -> Node {
Node::PushSymbol(node)
}
}
impl StackGraph {
pub fn add_push_symbol_node(
&mut self,
id: NodeID,
symbol: Handle<Symbol>,
is_reference: bool,
) -> Option<Handle<Node>> {
let node = PushSymbolNode {
id,
symbol,
_scope: NodeID::default(),
is_reference,
};
self.add_node(id, node.into())
}
}
impl PushSymbolNode {
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushSymbolNode<'a> {
wrapped: &'a PushSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"reference"
} else {
"push"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
#[repr(C)]
pub struct RootNode {
id: NodeID,
_symbol: ControlledOption<Handle<Symbol>>,
_scope: NodeID,
_is_clickable: bool,
}
impl From<RootNode> for Node {
fn from(node: RootNode) -> Node {
Node::Root(node)
}
}
impl RootNode {
fn new() -> RootNode {
RootNode {
id: NodeID::root(),
_symbol: ControlledOption::none(),
_scope: NodeID::default(),
_is_clickable: false,
}
}
}
impl Display for RootNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[root]")
}
}
struct NodeIDHandles {
files: SupplementalArena<File, Vec<Option<Handle<Node>>>>,
}
impl NodeIDHandles {
fn new() -> NodeIDHandles {
NodeIDHandles {
files: SupplementalArena::new(),
}
}
fn try_handle_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = self.files.get(node_id.file().unwrap())?;
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
return None;
}
file_entry[node_index]
}
fn handle_for_id(&mut self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = &mut self.files[node_id.file().unwrap()];
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
file_entry.resize(node_index + 1, None);
}
file_entry[node_index]
}
fn set_handle_for_id(&mut self, node_id: NodeID, handle: Handle<Node>) {
let file_entry = &mut self.files[node_id.file().unwrap()];
let node_index = node_id.local_id as usize;
file_entry[node_index] = Some(handle);
}
fn unused_id(&mut self, file: Handle<File>) -> NodeID {
let local_id = self
.files
.get(file)
.map(|file_entry| file_entry.len() as u32)
.unwrap_or(0);
NodeID::new_in_file(file, local_id)
}
fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
let file_entry = match self.files.get(file) {
Some(file_entry) => file_entry,
None => return Either::Left(std::iter::empty()),
};
Either::Right(file_entry.iter().filter_map(|entry| *entry))
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Edge {
pub source: Handle<Node>,
pub sink: Handle<Node>,
pub precedence: i32,
}
struct OutgoingEdge {
sink: Handle<Node>,
precedence: i32,
}
impl StackGraph {
pub fn add_edge(&mut self, source: Handle<Node>, sink: Handle<Node>, precedence: i32) {
let edges = &mut self.outgoing_edges[source];
if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
edges.insert(index, OutgoingEdge { sink, precedence });
}
}
pub fn remove_edge(&mut self, source: Handle<Node>, sink: Handle<Node>) {
let edges = &mut self.outgoing_edges[source];
if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
edges.remove(index);
}
}
pub fn outgoing_edges(&self, source: Handle<Node>) -> impl Iterator<Item = Edge> + '_ {
match self.outgoing_edges.get(source) {
Some(edges) => Either::Right(edges.iter().map(move |o| Edge {
source,
sink: o.sink,
precedence: o.precedence,
})),
None => Either::Left(std::iter::empty()),
}
}
}
pub struct StackGraph {
interned_strings: InternedStringContent,
pub(crate) symbols: Arena<Symbol>,
symbol_handles: FxHashMap<&'static str, Handle<Symbol>>,
pub(crate) files: Arena<File>,
file_handles: FxHashMap<&'static str, Handle<File>>,
pub(crate) nodes: Arena<Node>,
node_id_handles: NodeIDHandles,
jump_to_node: Handle<Node>,
root_node: Handle<Node>,
outgoing_edges: SupplementalArena<Node, SmallVec<[OutgoingEdge; 8]>>,
}
impl StackGraph {
pub fn new() -> StackGraph {
StackGraph::default()
}
}
impl Default for StackGraph {
fn default() -> StackGraph {
let mut nodes = Arena::new();
let root_node = nodes.add(RootNode::new().into());
let jump_to_node = nodes.add(JumpToNode::new().into());
StackGraph {
interned_strings: InternedStringContent::new(),
symbols: Arena::new(),
symbol_handles: FxHashMap::default(),
files: Arena::new(),
file_handles: FxHashMap::default(),
nodes,
node_id_handles: NodeIDHandles::new(),
jump_to_node,
root_node,
outgoing_edges: SupplementalArena::new(),
}
}
}