introduce bespoke Heap type for in-heap partial strings
This commit is contained in:
@@ -23,15 +23,117 @@ use indexmap::IndexSet;
|
||||
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtomCell {
|
||||
name: B48,
|
||||
arity: B8,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
is_inlined: bool,
|
||||
#[allow(unused)]
|
||||
tag: B5,
|
||||
}
|
||||
|
||||
const INLINED_ATOM_MAX_LEN: usize = 6;
|
||||
|
||||
const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::<AtomCell>());
|
||||
const_assert!(mem::size_of::<AtomCell>() == 8);
|
||||
|
||||
const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::<Atom>());
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
impl AtomCell {
|
||||
#[inline]
|
||||
pub fn new_static(index: u64) -> Self {
|
||||
// upper 23 bits of index must be 0
|
||||
debug_assert!(index & !((1 << 49) - 1) == 0);
|
||||
AtomCell::new()
|
||||
.with_name(index)
|
||||
.with_arity(0u8)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(false)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_inlined(string: &str, arity: u8) -> Self {
|
||||
debug_assert!(string.len() <= INLINED_ATOM_MAX_LEN);
|
||||
|
||||
let mut string_buf: [u8; 8] = [0u8; 8];
|
||||
string_buf[.. string.len()].copy_from_slice(string.as_bytes());
|
||||
let encoding = u64::from_le_bytes(string_buf);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(encoding)
|
||||
.with_arity(arity)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(true)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_char_inlined(c: char) -> Self {
|
||||
let mut char_buf = [0u8;8];
|
||||
c.encode_utf8(&mut char_buf);
|
||||
|
||||
let encoding = u64::from_le_bytes(char_buf);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(encoding)
|
||||
.with_arity(0u8)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(true)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_with(atom_index: u64, arity: u8) -> Self {
|
||||
debug_assert!((arity as usize) <= MAX_ARITY);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(atom_index >> 1)
|
||||
.with_arity(arity)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
.with_is_inlined(atom_index & 1 == 1)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(self) -> Atom {
|
||||
Atom { index: (self.name() << 1) | self.is_inlined() as u64 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_arity(self) -> usize {
|
||||
self.arity() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
||||
(self.get_name(), self.get_arity())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
|
||||
|
||||
// populate these in STRINGS so they can be used from build_functor
|
||||
const _: Atom = atom!(".");
|
||||
const _: Atom = atom!("[]");
|
||||
|
||||
impl<'a> From<&'a Atom> for Atom {
|
||||
#[inline]
|
||||
fn from(atom: &'a Atom) -> Self {
|
||||
@@ -39,17 +141,6 @@ impl<'a> From<&'a Atom> for Atom {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Atom {
|
||||
#[inline]
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
atom!("true")
|
||||
} else {
|
||||
atom!("false")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl indexmap::Equivalent<Atom> for str {
|
||||
fn equivalent(&self, key: &Atom) -> bool {
|
||||
&*key.as_str() == self
|
||||
@@ -120,24 +211,25 @@ impl Hash for Atom {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.as_str().hash(hasher)
|
||||
// hasher.write_usize(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AtomString<'a> {
|
||||
Static(&'a str),
|
||||
Inlined([u8;8]),
|
||||
Dynamic(AtomTableRef<str>),
|
||||
}
|
||||
|
||||
impl AtomString<'_> {
|
||||
pub fn map<F>(self, f: F) -> Self
|
||||
where
|
||||
for<'a> F: FnOnce(&'a str) -> &'a str,
|
||||
{
|
||||
match self {
|
||||
Self::Static(reference) => Self::Static(f(reference)),
|
||||
Self::Dynamic(guard) => Self::Dynamic(AtomTableRef::map(guard, f)),
|
||||
}
|
||||
fn inlined_to_str<'a>(bytes: &'a [u8;8]) -> &'a str {
|
||||
// allow the '\0\' atom to be represented as the 0-valued inlined atom
|
||||
let slice_len = if bytes[0] == 0 {
|
||||
1
|
||||
} else {
|
||||
bytes.iter().position(|&b| b == 0u8).unwrap_or(INLINED_ATOM_MAX_LEN)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
str::from_utf8_unchecked(&bytes[..slice_len])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +250,7 @@ impl std::ops::Deref for AtomString<'_> {
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Static(reference) => reference,
|
||||
Self::Inlined(inlined) => inlined_to_str(&inlined),
|
||||
Self::Dynamic(guard) => guard.deref(),
|
||||
}
|
||||
}
|
||||
@@ -175,13 +268,32 @@ impl rustyline::completion::Candidate for AtomString<'_> {
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline(always)]
|
||||
pub fn is_static(self) -> bool {
|
||||
(self.index as usize) < STRINGS.len() << 3
|
||||
#[inline]
|
||||
fn new_inlined(string: &str) -> Self {
|
||||
AtomCell::new_inlined(string, 0).get_name()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<AtomData>> {
|
||||
fn is_static(self) -> bool {
|
||||
if self.is_inlined() {
|
||||
true
|
||||
} else {
|
||||
(self.flat_index() as usize) < STRINGS.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn flat_index(self) -> u64 {
|
||||
self.index >> 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_inlined(self) -> bool {
|
||||
self.index & 1 == 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(self) -> Option<AtomTableRef<AtomData>> {
|
||||
if self.is_static() {
|
||||
None
|
||||
} else {
|
||||
@@ -192,7 +304,7 @@ impl Atom {
|
||||
let ptr = buf
|
||||
.block
|
||||
.base
|
||||
.add((self.index as usize) - (STRINGS.len() << 3));
|
||||
.add(self.flat_index() as usize - STRINGS.len());
|
||||
// TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513
|
||||
let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData);
|
||||
let len = atom_data.header.len();
|
||||
@@ -208,8 +320,11 @@ impl Atom {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(self) -> usize {
|
||||
if self.is_static() {
|
||||
STRINGS[(self.index >> 3) as usize].len()
|
||||
if let Some(s) = self.inlined_str() {
|
||||
s.len()
|
||||
} else if self.is_static() {
|
||||
let index = self.flat_index();
|
||||
STRINGS[index as usize].len()
|
||||
} else {
|
||||
let len: u64 = self.as_ptr().unwrap().header.len();
|
||||
len as usize
|
||||
@@ -220,11 +335,6 @@ impl Atom {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
self.index >> 3
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
let s = self.as_str();
|
||||
let mut it = s.chars();
|
||||
@@ -239,14 +349,26 @@ impl Atom {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inlined_str<'a>(&self) -> Option<AtomString<'a>> {
|
||||
if self.is_inlined() {
|
||||
Some(AtomString::Inlined(self.flat_index().to_le_bytes()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> AtomString<'static> {
|
||||
if self.is_static() {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
if let Some(s) = self.inlined_str() {
|
||||
s
|
||||
} else if self.is_static() {
|
||||
let index = self.flat_index() as usize;
|
||||
AtomString::Static(STRINGS[index])
|
||||
} else if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| &ptr.data))
|
||||
} else {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
AtomString::Static(STRINGS[(self.index >> 1) as usize])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +464,10 @@ impl AtomTable {
|
||||
}
|
||||
|
||||
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
||||
if 0 < string.len() && string.len() <= INLINED_ATOM_MAX_LEN {
|
||||
return Atom::new_inlined(string);
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut block_epoch = atom_table.inner.read();
|
||||
let mut table_epoch = block_epoch.table.read();
|
||||
@@ -390,9 +516,14 @@ impl AtomTable {
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
let atom = Atom {
|
||||
index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64,
|
||||
};
|
||||
let atom = AtomCell::new()
|
||||
.with_name((STRINGS.len() + len_ptr as usize - ptr_base) as u64)
|
||||
.with_arity(0)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
.with_is_inlined(false)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
.get_name();
|
||||
|
||||
let mut table = table_epoch.clone();
|
||||
table.insert(atom);
|
||||
@@ -410,18 +541,21 @@ impl AtomTable {
|
||||
unsafe impl Send for AtomTable {}
|
||||
unsafe impl Sync for AtomTable {}
|
||||
|
||||
/*
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtomCell {
|
||||
name: B46,
|
||||
name: B48,
|
||||
arity: B10,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
tag: B6,
|
||||
inlined: bool,
|
||||
#[allow(unused)]
|
||||
tag: B3,
|
||||
}
|
||||
|
||||
impl AtomCell {
|
||||
@@ -463,3 +597,4 @@ impl AtomCell {
|
||||
(Atom::from((self.get_index() as u64) << 3), self.get_arity())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user