[Outputs of Build Script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script) states that only files in $OUT_DIR may be modified
367 lines
7.9 KiB
Rust
367 lines
7.9 KiB
Rust
use crate::parser::ast::MAX_ARITY;
|
|
use crate::raw_block::*;
|
|
use crate::types::*;
|
|
|
|
use std::borrow::Borrow;
|
|
use std::cmp::Ordering;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::mem;
|
|
use std::ptr;
|
|
use std::slice;
|
|
use std::str;
|
|
|
|
use indexmap::IndexSet;
|
|
|
|
use modular_bitfield::prelude::*;
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub struct Atom {
|
|
pub index: usize,
|
|
}
|
|
|
|
const_assert!(mem::size_of::<Atom>() == 8);
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
|
|
|
|
impl<'a> From<&'a Atom> for Atom {
|
|
#[inline]
|
|
fn from(atom: &'a Atom) -> Self {
|
|
*atom
|
|
}
|
|
}
|
|
|
|
impl From<bool> for Atom {
|
|
#[inline]
|
|
fn from(value: bool) -> Self {
|
|
if value { atom!("true") } else { atom!("false") }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
use std::cell::RefCell;
|
|
|
|
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
|
const ATOM_TABLE_ALIGN: usize = 8;
|
|
|
|
#[cfg(test)]
|
|
thread_local! {
|
|
static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut());
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut();
|
|
|
|
#[cfg(test)]
|
|
fn set_atom_tbl_buf_base(ptr: *const u8) {
|
|
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| {
|
|
*atom_table_buf_base.borrow_mut() = ptr;
|
|
});
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
|
|
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow())
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
fn set_atom_tbl_buf_base(ptr: *const u8) {
|
|
unsafe {
|
|
ATOM_TABLE_BUF_BASE = ptr;
|
|
}
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
|
|
unsafe { ATOM_TABLE_BUF_BASE }
|
|
}
|
|
|
|
impl RawBlockTraits for AtomTable {
|
|
#[inline]
|
|
fn init_size() -> usize {
|
|
ATOM_TABLE_INIT_SIZE
|
|
}
|
|
|
|
#[inline]
|
|
fn align() -> usize {
|
|
ATOM_TABLE_ALIGN
|
|
}
|
|
}
|
|
|
|
#[bitfield]
|
|
#[derive(Copy, Clone, Debug)]
|
|
struct AtomHeader {
|
|
#[allow(unused)] m: bool,
|
|
len: B50,
|
|
#[allow(unused)] padding: B13,
|
|
}
|
|
|
|
impl AtomHeader {
|
|
fn build_with(len: u64) -> Self {
|
|
AtomHeader::new().with_len(len).with_m(false)
|
|
}
|
|
}
|
|
|
|
impl Borrow<str> for Atom {
|
|
#[inline]
|
|
fn borrow(&self) -> &str {
|
|
self.as_str()
|
|
}
|
|
}
|
|
|
|
impl Hash for Atom {
|
|
#[inline]
|
|
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
|
self.as_str().hash(hasher)
|
|
// hasher.write_usize(self.index)
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! is_char {
|
|
($s:expr) => {
|
|
!$s.is_empty() && $s.chars().nth(1).is_none()
|
|
};
|
|
}
|
|
|
|
impl Atom {
|
|
#[inline]
|
|
pub fn buf(self) -> *const u8 {
|
|
let ptr = self.as_ptr();
|
|
|
|
if ptr.is_null() {
|
|
return ptr::null();
|
|
}
|
|
|
|
(ptr as usize + mem::size_of::<AtomHeader>()) as *const u8
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn is_static(self) -> bool {
|
|
self.index < STRINGS.len() << 3
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn as_ptr(self) -> *const u8 {
|
|
if self.is_static() {
|
|
ptr::null()
|
|
} else {
|
|
(get_atom_tbl_buf_base() as usize + self.index - (STRINGS.len() << 3)) as *const u8
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn from(index: usize) -> Self {
|
|
Self { index }
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn len(self) -> usize {
|
|
if self.is_static() {
|
|
STRINGS[self.index >> 3].len()
|
|
} else {
|
|
unsafe { ptr::read(self.as_ptr() as *const AtomHeader).len() as _ }
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn flat_index(self) -> u64 {
|
|
(self.index >> 3) as u64
|
|
}
|
|
|
|
pub fn as_char(self) -> Option<char> {
|
|
let s = self.as_str();
|
|
let mut it = s.chars();
|
|
|
|
let c1 = it.next();
|
|
let c2 = it.next();
|
|
|
|
if c2.is_none() { c1 } else { None }
|
|
}
|
|
|
|
#[inline]
|
|
pub fn chars(&self) -> str::Chars {
|
|
self.as_str().chars()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn as_str(&self) -> &str {
|
|
unsafe {
|
|
let ptr = self.as_ptr();
|
|
|
|
if ptr.is_null() {
|
|
return STRINGS[self.index >> 3];
|
|
}
|
|
|
|
let header = ptr::read::<AtomHeader>(ptr as *const _);
|
|
let len = header.len() as usize;
|
|
let buf = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
|
|
|
|
str::from_utf8_unchecked(slice::from_raw_parts(buf, len))
|
|
}
|
|
}
|
|
|
|
pub fn defrock_brackets(&self, atom_tbl: &mut AtomTable) -> Self {
|
|
let s = self.as_str();
|
|
|
|
let s = if s.starts_with('(') && s.ends_with(')') {
|
|
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
|
|
} else {
|
|
return *self;
|
|
};
|
|
|
|
atom_tbl.build_with(s)
|
|
}
|
|
}
|
|
|
|
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
|
|
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
|
let str_ptr = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
|
|
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len());
|
|
}
|
|
|
|
impl PartialOrd for Atom {
|
|
#[inline]
|
|
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for Atom {
|
|
#[inline]
|
|
fn cmp(&self, other: &Atom) -> Ordering {
|
|
self.as_str().cmp(other.as_str())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct AtomTable {
|
|
block: RawBlock<AtomTable>,
|
|
pub table: IndexSet<Atom>,
|
|
}
|
|
|
|
impl Drop for AtomTable {
|
|
fn drop(&mut self) {
|
|
self.block.deallocate();
|
|
}
|
|
}
|
|
|
|
impl AtomTable {
|
|
#[inline]
|
|
pub fn new() -> Self {
|
|
let table = Self {
|
|
block: RawBlock::new(),
|
|
table: IndexSet::new(),
|
|
};
|
|
|
|
set_atom_tbl_buf_base(table.block.base);
|
|
table
|
|
}
|
|
|
|
#[inline]
|
|
pub fn buf(&self) -> *const u8 {
|
|
self.block.base as *const u8
|
|
}
|
|
|
|
#[inline]
|
|
pub fn top(&self) -> *const u8 {
|
|
self.block.top
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn lookup_str(&self, string: &str) -> Option<Atom> {
|
|
STATIC_ATOMS_MAP.get(string).or_else(|| self.table.get(string)).cloned()
|
|
}
|
|
|
|
pub fn build_with(&mut self, string: &str) -> Atom {
|
|
if let Some(atom) = self.lookup_str(string) {
|
|
return atom;
|
|
}
|
|
|
|
unsafe {
|
|
let size = mem::size_of::<AtomHeader>() + string.len();
|
|
let align_offset = 8 * mem::align_of::<AtomHeader>();
|
|
let size = (size & !(align_offset - 1)) + align_offset;
|
|
|
|
let len_ptr = {
|
|
let mut ptr;
|
|
|
|
loop {
|
|
ptr = self.block.alloc(size);
|
|
|
|
if ptr.is_null() {
|
|
self.block.grow();
|
|
set_atom_tbl_buf_base(self.block.base);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ptr
|
|
};
|
|
|
|
let ptr_base = self.block.base as usize;
|
|
|
|
write_to_ptr(string, len_ptr);
|
|
|
|
let atom = Atom {
|
|
index: (STRINGS.len() << 3) + len_ptr as usize - ptr_base,
|
|
};
|
|
|
|
self.table.insert(atom);
|
|
|
|
atom
|
|
}
|
|
}
|
|
}
|
|
|
|
#[bitfield]
|
|
#[repr(u64)]
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct AtomCell {
|
|
name: B46,
|
|
arity: B10,
|
|
#[allow(unused)] f: bool,
|
|
#[allow(unused)] m: bool,
|
|
#[allow(unused)] tag: B6,
|
|
}
|
|
|
|
impl AtomCell {
|
|
#[inline]
|
|
pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
|
|
if arity > 0 {
|
|
debug_assert!(arity as usize <= MAX_ARITY);
|
|
|
|
AtomCell::new()
|
|
.with_name(name)
|
|
.with_arity(arity)
|
|
.with_f(false)
|
|
.with_tag(tag as u8)
|
|
} else {
|
|
AtomCell::new()
|
|
.with_name(name)
|
|
.with_f(false)
|
|
.with_tag(tag as u8)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_index(self) -> usize {
|
|
self.name() as usize
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_name(self) -> Atom {
|
|
Atom::from(self.get_index() << 3)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_arity(self) -> usize {
|
|
self.arity() as usize
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
|
(Atom::from(self.get_index() << 3), self.get_arity())
|
|
}
|
|
}
|