use std::{fmt, hash, mem};
type ErasedPtr = *const u8;
use crate::{
RawSyntaxKind,
green::{GreenNode, GreenToken},
text::TextSize,
util::NodeOrToken,
};
pub(super) type GreenElement = NodeOrToken<GreenNode, GreenToken>;
pub(crate) type GreenElementRef<'a> = NodeOrToken<&'a GreenNode, &'a GreenToken>;
#[repr(transparent)]
pub(crate) struct PackedGreenElement {
ptr: ErasedPtr,
}
impl From<GreenNode> for GreenElement {
#[inline]
fn from(node: GreenNode) -> GreenElement {
NodeOrToken::Node(node)
}
}
impl<'a> From<&'a GreenNode> for GreenElementRef<'a> {
#[inline]
fn from(node: &'a GreenNode) -> GreenElementRef<'a> {
NodeOrToken::Node(node)
}
}
impl From<GreenNode> for PackedGreenElement {
#[inline]
fn from(node: GreenNode) -> PackedGreenElement {
unsafe { mem::transmute(node) }
}
}
impl From<GreenToken> for GreenElement {
#[inline]
fn from(token: GreenToken) -> GreenElement {
NodeOrToken::Token(token)
}
}
impl<'a> From<&'a GreenToken> for GreenElementRef<'a> {
#[inline]
fn from(token: &'a GreenToken) -> GreenElementRef<'a> {
NodeOrToken::Token(token)
}
}
impl From<GreenToken> for PackedGreenElement {
#[inline]
fn from(token: GreenToken) -> PackedGreenElement {
unsafe { mem::transmute(token) }
}
}
impl GreenElement {
#[inline]
pub fn kind(&self) -> RawSyntaxKind {
self.as_ref().kind()
}
#[inline]
pub fn text_len(&self) -> TextSize {
self.as_ref().text_len()
}
}
impl GreenElementRef<'_> {
#[inline]
pub fn kind(&self) -> RawSyntaxKind {
match self {
NodeOrToken::Node(it) => it.kind(),
NodeOrToken::Token(it) => it.kind(),
}
}
#[inline]
pub fn text_len(self) -> TextSize {
match self {
NodeOrToken::Node(it) => it.text_len(),
NodeOrToken::Token(it) => it.text_len(),
}
}
}
impl From<GreenElement> for PackedGreenElement {
fn from(element: GreenElement) -> Self {
match element {
NodeOrToken::Node(node) => node.into(),
NodeOrToken::Token(token) => token.into(),
}
}
}
impl From<PackedGreenElement> for GreenElement {
fn from(element: PackedGreenElement) -> Self {
if element.is_node() {
NodeOrToken::Node(element.into_node().unwrap())
} else {
NodeOrToken::Token(element.into_token().unwrap())
}
}
}
impl PackedGreenElement {
pub(crate) fn is_node(&self) -> bool {
self.ptr.addr() & super::token::IS_TOKEN_TAG == 0
}
pub(crate) fn as_node(&self) -> Option<&GreenNode> {
if self.is_node() {
unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenNode)) }
} else {
None
}
}
pub(crate) fn into_node(self) -> Option<GreenNode> {
if self.is_node() {
unsafe { Some(mem::transmute::<Self, GreenNode>(self)) }
} else {
None
}
}
pub(crate) fn as_token(&self) -> Option<&GreenToken> {
if !self.is_node() {
unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenToken)) }
} else {
None
}
}
pub(crate) fn into_token(self) -> Option<GreenToken> {
if !self.is_node() {
unsafe { Some(mem::transmute::<Self, GreenToken>(self)) }
} else {
None
}
}
pub(crate) fn as_ref(&self) -> GreenElementRef<'_> {
if self.is_node() {
NodeOrToken::Node(self.as_node().unwrap())
} else {
NodeOrToken::Token(self.as_token().unwrap())
}
}
}
impl fmt::Debug for PackedGreenElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_node() {
self.as_node().unwrap().fmt(f)
} else {
self.as_token().unwrap().fmt(f)
}
}
}
impl Eq for PackedGreenElement {}
impl PartialEq for PackedGreenElement {
fn eq(&self, other: &Self) -> bool {
self.as_node() == other.as_node() && self.as_token() == other.as_token()
}
}
impl hash::Hash for PackedGreenElement {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
if self.is_node() {
self.as_node().unwrap().hash(state)
} else {
self.as_token().unwrap().hash(state)
}
}
}
impl Drop for PackedGreenElement {
fn drop(&mut self) {
if self.is_node() {
PackedGreenElement { ptr: self.ptr }.into_node();
} else {
PackedGreenElement { ptr: self.ptr }.into_token();
}
}
}
unsafe impl Send for PackedGreenElement
where
GreenToken: Send,
GreenNode: Send,
{
}
unsafe impl Sync for PackedGreenElement
where
GreenToken: Sync,
GreenNode: Sync,
{
}