use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, vec::Vec};
pub type ValueId = u32;
pub type SymId = u32;
pub const NO_SYM: SymId = u32::MAX;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Kind {
Nil,
True,
False,
Fixnum,
Bignum,
Float,
Bytes,
Str,
Symbol,
Regexp,
Array,
Hash,
Struct,
Object,
Class,
Module,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Flags(pub u8);
impl Flags {
pub const NONE: Self = Self(0);
pub const OLD_MODULE: Self = Self(1 << 0);
pub const HAS_DEFAULT: Self = Self(1 << 1);
pub const USER_CLASS: Self = Self(1 << 2);
pub const NEGATIVE: Self = Self(1 << 3);
pub const EXTENDED: Self = Self(1 << 4);
pub const USER_DEFINED: Self = Self(1 << 5);
pub const DATA: Self = Self(1 << 6);
pub const USER_MARSHAL: Self = Self(1 << 7);
#[inline]
#[must_use]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn with(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Node {
pub kind: Kind,
pub flags: Flags,
_pad: u16,
pub class: SymId,
pub a: u32,
pub b: u32,
}
impl Node {
#[inline]
const fn new(kind: Kind) -> Self {
Self {
kind,
flags: Flags::NONE,
_pad: 0,
class: NO_SYM,
a: 0,
b: 0,
}
}
}
pub struct Arena<'a> {
pub(crate) nodes: Vec<Node>,
pub(crate) children: Vec<ValueId>,
pub(crate) members: Vec<(SymId, ValueId)>,
pub(crate) symbols: Vec<Cow<'a, [u8]>>,
pub(crate) blobs: Vec<Cow<'a, [u8]>>,
pub(crate) links: Vec<ValueId>,
pub(crate) extensions: Vec<(ValueId, SymId)>,
pub(crate) custom_encodings: Vec<(ValueId, u32)>,
pub(crate) root: ValueId,
pub(crate) sym_intern: BTreeMap<Box<[u8]>, SymId>,
}
#[allow(clippy::cast_possible_truncation)]
impl<'a> Arena<'a> {
pub(crate) const fn new() -> Self {
Self {
nodes: Vec::new(),
children: Vec::new(),
members: Vec::new(),
symbols: Vec::new(),
blobs: Vec::new(),
links: Vec::new(),
extensions: Vec::new(),
custom_encodings: Vec::new(),
root: 0,
sym_intern: BTreeMap::new(),
}
}
#[inline]
#[must_use]
pub const fn root(&self) -> ValueId {
self.root
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
#[inline]
#[must_use]
pub fn node(&self, id: ValueId) -> &Node {
&self.nodes[id as usize]
}
#[inline]
#[must_use]
pub fn symbol_bytes(&self, id: SymId) -> &[u8] {
&self.symbols[id as usize]
}
#[inline]
#[must_use]
pub fn symbol_str(&self, id: SymId) -> Option<&str> {
core::str::from_utf8(self.symbol_bytes(id)).ok()
}
#[inline]
#[must_use]
pub fn blob(&self, idx: u32) -> &[u8] {
&self.blobs[idx as usize]
}
#[inline]
#[must_use]
pub fn class_of(&self, id: ValueId) -> Option<SymId> {
let sym = self.node(id).class;
(sym != NO_SYM).then_some(sym)
}
#[inline]
pub fn extensions_of(&self, id: ValueId) -> impl Iterator<Item = SymId> + '_ {
self.extensions.iter().filter(move |(v, _)| *v == id).map(|(_, m)| *m)
}
#[inline]
#[must_use]
pub fn custom_encoding_of(&self, id: ValueId) -> Option<&[u8]> {
let &(_, blob) = self.custom_encodings.iter().find(|(v, _)| *v == id)?;
Some(self.blob(blob))
}
pub(crate) fn push_node(&mut self, node: Node) -> ValueId {
let id = self.nodes.len() as u32;
self.nodes.push(node);
id
}
pub(crate) fn set_node(&mut self, id: ValueId, node: Node) {
self.nodes[id as usize] = node;
}
pub(crate) fn push_blob(&mut self, bytes: Cow<'a, [u8]>) -> u32 {
let idx = self.blobs.len() as u32;
self.blobs.push(bytes);
idx
}
pub(crate) fn push_link_entry(&mut self, id: ValueId) {
self.links.push(id);
}
pub(crate) fn resolve_link(&self, idx: u32) -> Option<ValueId> {
self.links.get(idx as usize).copied()
}
pub(crate) fn intern_symbol(&mut self, bytes: Cow<'a, [u8]>) -> SymId {
if let Some(&id) = self.sym_intern.get(bytes.as_ref()) {
return id;
}
let id = self.symbols.len() as u32;
self.sym_intern.insert(Box::from(bytes.as_ref()), id);
self.symbols.push(bytes);
id
}
pub(crate) fn reserve_children(&mut self, n: usize) -> u32 {
let start = self.children.len() as u32;
self.children.resize(self.children.len() + n, 0);
start
}
pub(crate) fn reserve_members(&mut self, n: usize) -> u32 {
let start = self.members.len() as u32;
self.members.resize(self.members.len() + n, (NO_SYM, 0));
start
}
pub(crate) fn add_extension(&mut self, id: ValueId, module: SymId) {
self.extensions.push((id, module));
}
pub(crate) fn add_custom_encoding(&mut self, id: ValueId, blob: u32) {
self.custom_encodings.push((id, blob));
}
#[must_use]
pub fn into_owned(self) -> Arena<'static> {
Arena {
nodes: self.nodes,
children: self.children,
members: self.members,
symbols: self.symbols.into_iter().map(|c| Cow::Owned(c.into_owned())).collect(),
blobs: self.blobs.into_iter().map(|c| Cow::Owned(c.into_owned())).collect(),
links: self.links,
extensions: self.extensions,
custom_encodings: self.custom_encodings,
root: self.root,
sym_intern: BTreeMap::new(),
}
}
}
impl Node {
#[inline]
pub(crate) const fn scalar(kind: Kind) -> Self {
Self::new(kind)
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
impl Arena<'static> {
#[must_use]
pub const fn builder() -> Self {
Self::new()
}
pub fn push_nil(&mut self) -> ValueId {
self.push_node(Node::scalar(Kind::Nil))
}
pub fn push_bool(&mut self, value: bool) -> ValueId {
self.push_node(Node::scalar(if value { Kind::True } else { Kind::False }))
}
pub fn push_fixnum(&mut self, value: i32) -> ValueId {
let mut node = Node::new(Kind::Fixnum);
node.a = value as u32;
self.push_node(node)
}
pub fn push_bignum(&mut self, negative: bool, magnitude_le: Vec<u8>) -> ValueId {
let idx = self.push_blob(Cow::Owned(magnitude_le));
let mut node = Node::new(Kind::Bignum);
node.a = idx;
if negative {
node.flags = node.flags.with(Flags::NEGATIVE);
}
self.push_node(node)
}
pub fn push_float(&mut self, ascii: Vec<u8>) -> ValueId {
let idx = self.push_blob(Cow::Owned(ascii));
let mut node = Node::new(Kind::Float);
node.a = idx;
self.push_node(node)
}
pub fn push_bytes(&mut self, bytes: Vec<u8>) -> ValueId {
let idx = self.push_blob(Cow::Owned(bytes));
let mut node = Node::new(Kind::Bytes);
node.a = idx;
self.push_node(node)
}
pub fn push_string(&mut self, text: alloc::string::String) -> ValueId {
self.push_str_with_encoding(text.into_bytes(), crate::encoding::ENCODING_UTF_8)
}
pub fn push_str_with_encoding(&mut self, bytes: Vec<u8>, encoding_id: u8) -> ValueId {
let idx = self.push_blob(Cow::Owned(bytes));
let mut node = Node::new(Kind::Str);
node.a = idx;
node.b = u32::from(encoding_id);
self.push_node(node)
}
pub fn push_str_with_encoding_name(&mut self, bytes: Vec<u8>, encoding_name: &[u8]) -> ValueId {
let idx = self.push_blob(Cow::Owned(bytes));
let mut node = Node::new(Kind::Str);
node.a = idx;
let id = Self::tag_encoding_name(encoding_name);
node.b = u32::from(id);
let value = self.push_node(node);
if id == crate::encoding::ENCODING_CUSTOM {
let blob = self.push_blob(Cow::Owned(encoding_name.to_vec()));
self.add_custom_encoding(value, blob);
}
value
}
pub fn push_symbol(&mut self, bytes: Vec<u8>) -> ValueId {
let sym = self.intern_symbol(Cow::Owned(bytes));
let mut node = Node::new(Kind::Symbol);
node.a = sym;
self.push_node(node)
}
pub fn push_regexp(&mut self, source: Vec<u8>, options: u8) -> ValueId {
let idx = self.push_blob(Cow::Owned(source));
let mut node = Node::new(Kind::Regexp);
node.a = idx;
node.b = u32::from(options);
self.push_node(node)
}
pub fn push_regexp_with_encoding_name(&mut self, source: Vec<u8>, options: u8, encoding_name: &[u8]) -> ValueId {
let idx = self.push_blob(Cow::Owned(source));
let mut node = Node::new(Kind::Regexp);
node.a = idx;
let id = Self::tag_encoding_name(encoding_name);
node.b = u32::from(options) | (u32::from(id) << 8);
let value = self.push_node(node);
if id == crate::encoding::ENCODING_CUSTOM {
let blob = self.push_blob(Cow::Owned(encoding_name.to_vec()));
self.add_custom_encoding(value, blob);
}
value
}
fn tag_encoding_name(encoding_name: &[u8]) -> u8 {
crate::encoding::encoding_id(encoding_name).unwrap_or(crate::encoding::ENCODING_CUSTOM)
}
pub fn push_array(&mut self, elements: &[ValueId]) -> ValueId {
let start = self.reserve_children(elements.len());
self.children[start as usize..start as usize + elements.len()].copy_from_slice(elements);
let mut node = Node::new(Kind::Array);
node.a = start;
node.b = elements.len() as u32;
self.push_node(node)
}
pub fn push_hash(&mut self, pairs: &[(ValueId, ValueId)], default: Option<ValueId>) -> ValueId {
let extra = usize::from(default.is_some());
let start = self.reserve_children(pairs.len() * 2 + extra);
for (i, &(k, v)) in pairs.iter().enumerate() {
self.children[start as usize + i * 2] = k;
self.children[start as usize + i * 2 + 1] = v;
}
let mut node = Node::new(Kind::Hash);
node.a = start;
node.b = pairs.len() as u32;
if let Some(default) = default {
self.children[start as usize + pairs.len() * 2] = default;
node.flags = node.flags.with(Flags::HAS_DEFAULT);
}
self.push_node(node)
}
pub fn push_struct(&mut self, class: Vec<u8>, members: &[(Vec<u8>, ValueId)]) -> ValueId {
let class_sym = self.intern_symbol(Cow::Owned(class));
let start = self.reserve_members(members.len());
for (i, (name, value)) in members.iter().enumerate() {
let sym = self.intern_symbol(Cow::Owned(name.clone()));
self.members[start as usize + i] = (sym, *value);
}
let mut node = Node::new(Kind::Struct);
node.class = class_sym;
node.a = start;
node.b = members.len() as u32;
self.push_node(node)
}
pub fn push_object(&mut self, class: Vec<u8>, ivars: &[(Vec<u8>, ValueId)]) -> ValueId {
let class_sym = self.intern_symbol(Cow::Owned(class));
let start = self.reserve_members(ivars.len());
for (i, (name, value)) in ivars.iter().enumerate() {
let sym = self.intern_symbol(Cow::Owned(name.clone()));
self.members[start as usize + i] = (sym, *value);
}
let mut node = Node::new(Kind::Object);
node.class = class_sym;
node.a = start;
node.b = ivars.len() as u32;
self.push_node(node)
}
pub fn push_class(&mut self, path: Vec<u8>) -> ValueId {
let idx = self.push_blob(Cow::Owned(path));
let mut node = Node::new(Kind::Class);
node.a = idx;
self.push_node(node)
}
pub fn push_module(&mut self, path: Vec<u8>, old: bool) -> ValueId {
let idx = self.push_blob(Cow::Owned(path));
let mut node = Node::new(Kind::Module);
node.a = idx;
if old {
node.flags = node.flags.with(Flags::OLD_MODULE);
}
self.push_node(node)
}
pub fn set_root(&mut self, id: ValueId) {
self.root = id;
}
}