use crate::atom::Atom;
use crate::term::{Term, binary::Binary, shared_binary::SharedBinary};
use super::{BoxedHeader, BoxedTag};
#[derive(Copy, Clone, Debug)]
pub struct Tuple {
ptr: *const u64,
}
impl Tuple {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::Tuple)?;
Some(Self { ptr })
}
pub fn arity(self) -> usize {
BoxedHeader::size(self.header())
}
pub fn get(self, index: usize) -> Option<Term> {
if index < self.arity() {
Some(Term::from_raw(self.word(1 + index)))
} else {
None
}
}
fn header(self) -> u64 {
self.word(0)
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct Cons {
ptr: *const u64,
}
impl Cons {
pub fn new(term: Term) -> Option<Self> {
if !term.is_list() {
return None;
}
Some(Self {
ptr: term.heap_ptr()?,
})
}
pub fn head(self) -> Term {
Term::from_raw(self.word(0))
}
pub fn tail(self) -> Term {
Term::from_raw(self.word(1))
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct Float {
ptr: *const u64,
}
impl Float {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::Float)?;
Some(Self { ptr })
}
pub fn value(self) -> f64 {
f64::from_bits(unsafe { *self.ptr.add(1) })
}
}
#[derive(Copy, Clone, Debug)]
pub struct BigInt {
ptr: *const u64,
}
impl BigInt {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::BigInt)?;
Some(Self { ptr })
}
pub fn is_negative(self) -> bool {
self.word(1) == super::BIGINT_NEGATIVE_SIGN
}
pub fn limb_count(self) -> usize {
self.word(2) as usize
}
pub fn limbs(self) -> &'static [u64] {
let count = self.limb_count();
unsafe { std::slice::from_raw_parts(self.ptr.add(3), count) }
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct Closure {
ptr: *const u64,
}
impl Closure {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::Closure)?;
let header = unsafe { *ptr };
let size = BoxedHeader::size(header);
if size < 6 {
return None;
}
let num_free = unsafe { *ptr.add(4) } as usize;
if size != 6 + num_free {
return None;
}
Some(Self { ptr })
}
pub fn module(self) -> Option<Atom> {
Term::from_raw(self.word(1)).as_atom()
}
pub fn function_index(self) -> u64 {
self.word(2)
}
pub fn arity(self) -> u8 {
self.word(3) as u8
}
pub fn num_free(self) -> usize {
self.word(4) as usize
}
pub fn generation(self) -> u64 {
self.word(5)
}
pub fn unique_id(self) -> u64 {
self.word(6)
}
pub fn free_var(self, index: usize) -> Option<Term> {
if index < self.num_free() {
Some(Term::from_raw(self.word(7 + index)))
} else {
None
}
}
pub fn is_export(self) -> bool {
self.generation() == super::EXPORT_FUN_GENERATION
}
pub fn export_function(self) -> Option<Atom> {
if self.is_export() {
Term::from_raw(self.word(2)).as_atom()
} else {
None
}
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct Map {
ptr: *const u64,
}
impl Map {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::Map)?;
Some(Self { ptr })
}
pub fn len(self) -> usize {
self.word(1) as usize
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn key(self, index: usize) -> Option<Term> {
if index < self.len() {
Some(Term::from_raw(self.word(2 + index)))
} else {
None
}
}
pub fn value(self, index: usize) -> Option<Term> {
if index < self.len() {
Some(Term::from_raw(self.word(2 + self.len() + index)))
} else {
None
}
}
pub fn get(self, key: Term) -> Option<Term> {
(0..self.len()).find_map(|index| {
if self.key(index) == Some(key) {
self.value(index)
} else {
None
}
})
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct ProcBin {
ptr: *const u64,
}
impl ProcBin {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::ProcBin)?;
let header = unsafe { *ptr };
if BoxedHeader::size(header) != 2 {
return None;
}
if unsafe { *ptr.add(2) } == 0 {
return None;
}
Some(Self { ptr })
}
pub fn as_bytes(self) -> &'static [u8] {
SharedBinary::bytes_from_raw_word(self.arc_ptr_word())
}
pub fn len(self) -> usize {
self.as_bytes().len()
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn shared_binary(self) -> SharedBinary {
SharedBinary::clone_from_raw_word(self.arc_ptr_word())
}
fn arc_ptr_word(self) -> u64 {
unsafe { *self.ptr.add(2) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct SubBinary {
ptr: *const u64,
}
impl SubBinary {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::SubBinary)?;
let header = unsafe { *ptr };
if BoxedHeader::size(header) != 4 {
return None;
}
let sub_binary = Self { ptr };
let parent_bytes = parent_bytes(sub_binary.parent())?;
let end = sub_binary.offset().checked_add(sub_binary.len())?;
if end > parent_bytes.len() {
return None;
}
Some(sub_binary)
}
pub fn parent(self) -> Term {
Term::from_raw(self.word(1))
}
pub fn len(self) -> usize {
self.word(3) as usize
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn as_bytes(self) -> &'static [u8] {
let bytes = parent_bytes(self.parent()).unwrap_or(&[]);
let start = self.offset();
let end = start.checked_add(self.len()).unwrap_or(start);
bytes.get(start..end).unwrap_or(&[])
}
fn offset(self) -> usize {
self.word(2) as usize
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
fn parent_bytes(parent: Term) -> Option<&'static [u8]> {
if let Some(binary) = Binary::new(parent) {
return Some(binary.as_bytes());
}
ProcBin::new(parent).map(ProcBin::as_bytes)
}
#[derive(Copy, Clone, Debug)]
pub struct Reference {
ptr: *const u64,
}
impl Reference {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::Reference)?;
if BoxedHeader::size(word_at(ptr, 0)) != 1 {
return None;
}
Some(Self { ptr })
}
pub fn id(self) -> u64 {
unsafe { *self.ptr.add(1) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct ExternalPid {
ptr: *const u64,
}
impl ExternalPid {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::ExternalPid)?;
if BoxedHeader::size(word_at(ptr, 0)) != 3
|| Term::from_raw(word_at(ptr, 1)).as_atom().is_none()
{
return None;
}
Some(Self { ptr })
}
pub fn node(self) -> Option<Atom> {
Term::from_raw(self.word(1)).as_atom()
}
pub fn pid_number(self) -> u64 {
self.word(2)
}
pub fn serial(self) -> u64 {
self.word(3)
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
#[derive(Copy, Clone, Debug)]
pub struct ExternalReference {
ptr: *const u64,
}
impl ExternalReference {
pub fn new(term: Term) -> Option<Self> {
let ptr = header_ptr(term, BoxedTag::ExternalReference)?;
if BoxedHeader::size(word_at(ptr, 0)) != 2
|| Term::from_raw(word_at(ptr, 1)).as_atom().is_none()
{
return None;
}
Some(Self { ptr })
}
pub fn node(self) -> Option<Atom> {
Term::from_raw(self.word(1)).as_atom()
}
pub fn id(self) -> u64 {
self.word(2)
}
fn word(self, offset: usize) -> u64 {
unsafe { *self.ptr.add(offset) }
}
}
fn word_at(ptr: *const u64, offset: usize) -> u64 {
unsafe { *ptr.add(offset) }
}
fn header_ptr(term: Term, expected_tag: BoxedTag) -> Option<*const u64> {
if !term.is_boxed() {
return None;
}
let ptr = term.heap_ptr()?;
let header = unsafe { *ptr };
if BoxedHeader::tag(header) == Some(expected_tag) {
Some(ptr)
} else {
None
}
}