1extern crate alloc;
29
30use core::fmt;
31use core::str;
32use alloc::vec::Vec;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct AtomIndex(pub u32);
39
40impl AtomIndex {
41 pub const INVALID: AtomIndex = AtomIndex(0);
42
43 pub fn new(index: u32) -> Self {
44 AtomIndex(index)
45 }
46
47 pub fn get(self) -> u32 {
48 self.0
49 }
50
51 pub fn is_valid(self) -> bool {
52 self.0 != 0
53 }
54}
55
56#[repr(u32)]
58pub enum AtomCopyOpt {
59 Reference = 0,
61 Copy = 1,
63 AlreadyExisting = 2,
65}
66
67#[repr(u32)]
69pub enum EnsureAtomsOpt {
70 Standard = 0,
72 LongEncoding = 1,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub enum AtomError {
79 NotFound,
81 AllocationFailed,
83 InvalidLength,
85 InvalidAtomData,
87 NullPointer,
89 InvalidIndex,
91}
92
93impl fmt::Display for AtomError {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self {
96 AtomError::NotFound => write!(f, "atom not found in table"),
97 AtomError::AllocationFailed => write!(f, "memory allocation failed"),
98 AtomError::InvalidLength => write!(f, "invalid atom length"),
99 AtomError::InvalidAtomData => write!(f, "invalid atom data or encoding"),
100 AtomError::NullPointer => write!(f, "unexpected null pointer from atom table"),
101 AtomError::InvalidIndex => write!(f, "invalid atom index"),
102 }
103 }
104}
105
106#[derive(Debug)]
108pub struct AtomRef<'a> {
109 data: &'a [u8],
110 index: AtomIndex,
111}
112
113impl<'a> AtomRef<'a> {
114 pub fn new(data: &'a [u8], index: AtomIndex) -> Self {
115 Self { data, index }
116 }
117
118 pub fn index(&self) -> AtomIndex {
120 self.index
121 }
122
123 pub fn as_bytes(&self) -> &[u8] {
125 self.data
126 }
127
128 pub fn as_str(&self) -> Result<&str, str::Utf8Error> {
130 str::from_utf8(self.data)
131 }
132
133 pub fn len(&self) -> usize {
135 self.data.len()
136 }
137
138 pub fn is_empty(&self) -> bool {
140 self.data.is_empty()
141 }
142}
143
144impl<'a> AsRef<[u8]> for AtomRef<'a> {
145 fn as_ref(&self) -> &[u8] {
146 self.data
147 }
148}
149
150impl<'a> PartialEq<[u8]> for AtomRef<'a> {
151 fn eq(&self, other: &[u8]) -> bool {
152 self.data == other
153 }
154}
155
156impl<'a> PartialEq<&[u8]> for AtomRef<'a> {
157 fn eq(&self, other: &&[u8]) -> bool {
158 self.data == *other
159 }
160}
161
162impl<'a> PartialEq<str> for AtomRef<'a> {
163 fn eq(&self, other: &str) -> bool {
164 self.data == other.as_bytes()
165 }
166}
167
168pub trait AtomTableOps {
175 fn count(&self) -> usize;
177
178 fn get_atom_string(&self, index: AtomIndex) -> Result<AtomRef<'_>, AtomError>;
180
181 fn ensure_atom(&self, atom_data: &[u8]) -> Result<AtomIndex, AtomError>;
183
184 fn find_atom(&self, atom_data: &[u8]) -> Result<AtomIndex, AtomError>;
186
187 fn ensure_atom_str(&self, atom_str: &str) -> Result<AtomIndex, AtomError> {
189 self.ensure_atom(atom_str.as_bytes())
190 }
191
192 fn find_atom_str(&self, atom_str: &str) -> Result<AtomIndex, AtomError> {
194 self.find_atom(atom_str.as_bytes())
195 }
196
197 fn atom_equals(&self, atom_index: AtomIndex, data: &[u8]) -> bool;
199
200 fn atom_equals_str(&self, atom_index: AtomIndex, s: &str) -> bool {
202 self.atom_equals(atom_index, s.as_bytes())
203 }
204
205 fn compare_atoms(&self, atom1: AtomIndex, atom2: AtomIndex) -> i32;
207
208 fn ensure_atoms_bulk(
210 &self,
211 atoms_data: &[u8],
212 count: usize,
213 encoding: EnsureAtomsOpt,
214 ) -> Result<Vec<AtomIndex>, AtomError>;
215}
216
217use core::ffi::c_void;
220use core::slice;
221
222#[repr(transparent)]
224pub struct AtomTable(*mut c_void);
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228enum AtomTableResult {
229 Ok,
230 NotFound,
231 AllocationFailed,
232 InvalidLength,
233}
234
235extern "C" {
237 fn atom_table_get_atom_string(
238 table: *mut c_void,
239 index: u32, out_size: *mut usize,
241 ) -> *const u8;
242
243 fn atom_table_ensure_atom(
244 table: *mut c_void,
245 atom_data: *const u8,
246 atom_len: usize,
247 opts: u32,
248 result: *mut u32, ) -> u32;
250
251 fn atom_table_ensure_atoms(
252 table: *mut c_void,
253 atoms: *const c_void,
254 count: usize,
255 translate_table: *mut u32, opt: u32,
257 ) -> u32;
258
259 fn atom_table_count(table: *mut c_void) -> usize;
260
261 fn atom_table_is_equal_to_atom_string(
262 table: *mut c_void,
263 atom_index: u32, string_data: *const u8,
265 string_len: usize,
266 ) -> bool;
267
268 fn atom_table_cmp_using_atom_index(
269 table: *mut c_void,
270 atom1: u32, atom2: u32, ) -> i32;
273
274 fn atomvm_get_global_atom_table() -> *mut c_void;
275}
276
277fn result_from_c(result: u32) -> AtomTableResult {
279 match result {
280 0 => AtomTableResult::Ok,
281 1 => AtomTableResult::NotFound,
282 2 => AtomTableResult::AllocationFailed,
283 3 => AtomTableResult::InvalidLength,
284 _ => AtomTableResult::AllocationFailed,
285 }
286}
287
288impl AtomTable {
289 pub unsafe fn from_raw(ptr: *mut c_void) -> Self {
294 AtomTable(ptr)
295 }
296
297 pub fn from_global() -> Self {
302 let ptr = unsafe { atomvm_get_global_atom_table() };
303 AtomTable(ptr)
304 }
305
306 pub unsafe fn as_raw(&self) -> *mut c_void {
311 self.0
312 }
313}
314
315impl AtomTableOps for AtomTable {
316 fn count(&self) -> usize {
317 unsafe { atom_table_count(self.0) }
318 }
319
320 fn get_atom_string(&self, index: AtomIndex) -> Result<AtomRef<'_>, AtomError> {
321 let mut size: usize = 0;
322 let ptr = unsafe { atom_table_get_atom_string(self.0, index.0, &mut size) };
323
324 if ptr.is_null() {
325 return Err(AtomError::InvalidIndex);
326 }
327
328 let data = unsafe { slice::from_raw_parts(ptr, size) };
329 Ok(AtomRef::new(data, index))
330 }
331
332 fn ensure_atom(&self, atom_data: &[u8]) -> Result<AtomIndex, AtomError> {
333 let mut result: u32 = 0; let status = unsafe {
335 atom_table_ensure_atom(
336 self.0,
337 atom_data.as_ptr(),
338 atom_data.len(),
339 AtomCopyOpt::Copy as u32,
340 &mut result,
341 )
342 };
343
344 match result_from_c(status) {
345 AtomTableResult::Ok => Ok(AtomIndex(result)),
346 AtomTableResult::NotFound => Err(AtomError::NotFound),
347 AtomTableResult::AllocationFailed => Err(AtomError::AllocationFailed),
348 AtomTableResult::InvalidLength => Err(AtomError::InvalidLength),
349 }
350 }
351
352 fn find_atom(&self, atom_data: &[u8]) -> Result<AtomIndex, AtomError> {
353 let mut result: u32 = 0; let status = unsafe {
355 atom_table_ensure_atom(
356 self.0,
357 atom_data.as_ptr(),
358 atom_data.len(),
359 AtomCopyOpt::AlreadyExisting as u32,
360 &mut result,
361 )
362 };
363
364 match result_from_c(status) {
365 AtomTableResult::Ok => Ok(AtomIndex(result)),
366 AtomTableResult::NotFound => Err(AtomError::NotFound),
367 AtomTableResult::AllocationFailed => Err(AtomError::AllocationFailed),
368 AtomTableResult::InvalidLength => Err(AtomError::InvalidLength),
369 }
370 }
371
372 fn atom_equals(&self, atom_index: AtomIndex, data: &[u8]) -> bool {
373 unsafe {
374 atom_table_is_equal_to_atom_string(
375 self.0,
376 atom_index.0, data.as_ptr(),
378 data.len(),
379 )
380 }
381 }
382
383 fn compare_atoms(&self, atom1: AtomIndex, atom2: AtomIndex) -> i32 {
384 unsafe { atom_table_cmp_using_atom_index(self.0, atom1.0, atom2.0) }
385 }
386
387 fn ensure_atoms_bulk(
388 &self,
389 atoms_data: &[u8],
390 count: usize,
391 encoding: EnsureAtomsOpt,
392 ) -> Result<Vec<AtomIndex>, AtomError> {
393 let mut translate_table: Vec<u32> = Vec::with_capacity(count); translate_table.resize(count, 0u32);
395
396 let status = unsafe {
397 atom_table_ensure_atoms(
398 self.0,
399 atoms_data.as_ptr() as *const c_void,
400 count,
401 translate_table.as_mut_ptr(),
402 encoding as u32,
403 )
404 };
405
406 match result_from_c(status) {
407 AtomTableResult::Ok => {
408 let result: Vec<AtomIndex> = translate_table.into_iter().map(AtomIndex).collect();
410 Ok(result)
411 }
412 AtomTableResult::NotFound => Err(AtomError::NotFound),
413 AtomTableResult::AllocationFailed => Err(AtomError::AllocationFailed),
414 AtomTableResult::InvalidLength => Err(AtomError::InvalidLength),
415 }
416 }
417}
418
419unsafe impl Send for AtomTable {}
421unsafe impl Sync for AtomTable {}
422
423pub mod atoms {
429 use super::*;
430
431 pub fn ensure_common_atoms<T: AtomTableOps>(table: &T) -> Result<(), AtomError> {
436 let common_atoms = [
437 "ok", "error", "true", "false", "undefined", "badarg", "nil",
438 "atom", "binary", "bitstring", "boolean", "float", "function",
439 "integer", "list", "map", "pid", "port", "reference", "tuple"
440 ];
441
442 for atom_name in &common_atoms {
443 table.ensure_atom_str(atom_name)?;
444 }
445
446 Ok(())
447 }
448
449 pub fn ok<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
451 table.ensure_atom_str("ok")
452 }
453
454 pub fn error<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
456 table.ensure_atom_str("error")
457 }
458
459 pub fn true_atom<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
461 table.ensure_atom_str("true")
462 }
463
464 pub fn false_atom<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
466 table.ensure_atom_str("false")
467 }
468
469 pub fn nil<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
471 table.ensure_atom_str("nil")
472 }
473
474 pub fn undefined<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
476 table.ensure_atom_str("undefined")
477 }
478
479 pub fn badarg<T: AtomTableOps>(table: &T) -> Result<AtomIndex, AtomError> {
481 table.ensure_atom_str("badarg")
482 }
483}