iris_ztd/
noun.rs

1use crate::belt::based_check;
2use crate::crypto::cheetah::F6lt;
3use alloc::{boxed::Box, collections::btree_map::BTreeMap, format, string::String, vec, vec::Vec};
4use bitvec::prelude::{BitSlice, BitVec, Lsb0};
5use core::fmt;
6use core::mem::MaybeUninit;
7use ibig::UBig;
8use num_traits::Zero;
9use serde::de::{Error as DeError, SeqAccess, Visitor};
10use serde::{ser::SerializeTuple, Serialize, Serializer};
11use serde::{Deserialize, Deserializer};
12
13use crate::{belt::Belt, crypto::cheetah::CheetahPoint, Digest};
14
15pub fn noun_serialize<T: NounEncode, S>(v: &T, serializer: S) -> Result<S::Ok, S::Error>
16where
17    S: Serializer,
18{
19    v.to_noun().serialize(serializer)
20}
21
22pub fn noun_deserialize<'de, T: NounDecode, D>(deserializer: D) -> Result<T, D::Error>
23where
24    D: Deserializer<'de>,
25{
26    let r = Noun::deserialize(deserializer)?;
27    T::from_noun(&r).ok_or_else(|| DeError::custom("unable to parse noun"))
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub enum Noun {
32    Atom(UBig),
33    Cell(Box<Noun>, Box<Noun>),
34}
35
36impl Noun {
37    #[allow(clippy::inherent_to_string_shadow_display)]
38    pub fn to_string(&self) -> String {
39        fn autocons(cell: &Noun) -> String {
40            match cell {
41                Noun::Atom(a) => format!("{}", a),
42                Noun::Cell(head, tail) => match &**tail {
43                    Noun::Cell(_, _) => format!("{} {}", autocons(head), autocons(tail)),
44                    Noun::Atom(a) if a.is_zero() => autocons(head),
45                    Noun::Atom(_) => format!("{} . {}", autocons(head), autocons(tail)),
46                },
47            }
48        }
49
50        match self {
51            Noun::Atom(a) => format!("{}", a),
52            Noun::Cell(head, tail) => {
53                format!("[{}]", autocons(&Noun::Cell(head.clone(), tail.clone())))
54            }
55        }
56    }
57}
58
59impl Serialize for Noun {
60    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61    where
62        S: Serializer,
63    {
64        match self {
65            Self::Atom(v) => serializer.serialize_str(&alloc::format!("{v:x}")),
66            Self::Cell(a, b) => {
67                let mut tup = serializer.serialize_tuple(2)?;
68                tup.serialize_element(a)?;
69                tup.serialize_element(b)?;
70                tup.end()
71            }
72        }
73    }
74}
75
76impl<'de> Deserialize<'de> for Noun {
77    fn deserialize<D>(de: D) -> Result<Self, D::Error>
78    where
79        D: Deserializer<'de>,
80    {
81        struct V;
82
83        impl<'de> Visitor<'de> for V {
84            type Value = Noun;
85
86            fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
87                f.write_str("atom or cell")
88            }
89
90            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
91            where
92                E: DeError,
93            {
94                let n = UBig::from_str_radix(s, 16).map_err(E::custom)?;
95                Ok(Noun::Atom(n))
96            }
97
98            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
99            where
100                A: SeqAccess<'de>,
101            {
102                let a = seq
103                    .next_element::<Noun>()?
104                    .ok_or_else(|| DeError::custom("cell missing car"))?;
105                let b = seq
106                    .next_element::<Noun>()?
107                    .ok_or_else(|| DeError::custom("cell missing cdr"))?;
108                Ok(Noun::Cell(Box::new(a), Box::new(b)))
109            }
110        }
111
112        de.deserialize_any(V)
113    }
114}
115
116impl fmt::Display for Noun {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "{}", self.to_string())
119    }
120}
121
122pub trait NounCode: NounEncode + NounDecode {}
123impl<T: NounEncode + NounDecode> NounCode for T {}
124
125pub trait NounEncode {
126    fn to_noun(&self) -> Noun;
127}
128
129pub trait NounDecode: Sized {
130    fn from_noun(noun: &Noun) -> Option<Self>;
131}
132
133fn atom(value: u64) -> Noun {
134    Noun::Atom(UBig::from(value))
135}
136
137fn cons(left: Noun, right: Noun) -> Noun {
138    Noun::Cell(Box::new(left), Box::new(right))
139}
140
141impl<T: NounEncode + ?Sized> NounEncode for &T {
142    fn to_noun(&self) -> Noun {
143        (**self).to_noun()
144    }
145}
146
147impl<T: NounEncode + ?Sized> NounEncode for Box<T> {
148    fn to_noun(&self) -> Noun {
149        (**self).to_noun()
150    }
151}
152
153impl<T: NounDecode> NounDecode for Box<T> {
154    fn from_noun(noun: &Noun) -> Option<Self> {
155        Some(Box::new(T::from_noun(noun)?))
156    }
157}
158
159impl NounEncode for Noun {
160    fn to_noun(&self) -> Noun {
161        self.clone()
162    }
163}
164
165impl NounDecode for Noun {
166    fn from_noun(noun: &Noun) -> Option<Self> {
167        Some(noun.clone())
168    }
169}
170
171impl NounEncode for () {
172    fn to_noun(&self) -> Noun {
173        atom(0)
174    }
175}
176
177impl NounDecode for () {
178    fn from_noun(noun: &Noun) -> Option<Self> {
179        if *noun == atom(0) {
180            Some(())
181        } else {
182            None
183        }
184    }
185}
186
187impl NounEncode for Belt {
188    fn to_noun(&self) -> Noun {
189        atom(self.0)
190    }
191}
192
193impl NounDecode for Belt {
194    fn from_noun(noun: &Noun) -> Option<Self> {
195        let Noun::Atom(a) = noun else {
196            return None;
197        };
198        let v = u64::try_from(a).ok()?;
199        if based_check(v) {
200            Some(Belt(v))
201        } else {
202            None
203        }
204    }
205}
206
207impl NounEncode for Digest {
208    fn to_noun(&self) -> Noun {
209        self.0.to_noun()
210    }
211}
212
213impl NounDecode for Digest {
214    fn from_noun(noun: &Noun) -> Option<Self> {
215        Some(Digest(<[Belt; 5]>::from_noun(noun)?))
216    }
217}
218
219impl NounEncode for CheetahPoint {
220    fn to_noun(&self) -> Noun {
221        (self.x.0, self.y.0, self.inf).to_noun()
222    }
223}
224
225impl NounDecode for CheetahPoint {
226    fn from_noun(noun: &Noun) -> Option<Self> {
227        let (x, y, inf) = NounDecode::from_noun(noun)?;
228        Some(Self {
229            x: F6lt(x),
230            y: F6lt(y),
231            inf,
232        })
233    }
234}
235
236macro_rules! impl_nounable_for_int {
237    ($($ty:ty),* $(,)?) => {
238        $(
239            impl NounEncode for $ty {
240                fn to_noun(&self) -> Noun {
241                    atom(*self as u64)
242                }
243            }
244
245            impl NounDecode for $ty {
246                fn from_noun(noun: &Noun) -> Option<$ty> {
247                    let Noun::Atom(a) = noun else {
248                        return None;
249                    };
250                    <$ty>::try_from(a).ok()
251                }
252            }
253        )*
254    };
255}
256
257impl_nounable_for_int!(i32, i64, isize, u32, u64, usize);
258
259impl NounEncode for bool {
260    fn to_noun(&self) -> Noun {
261        atom(if *self { 0 } else { 1 })
262    }
263}
264
265impl NounDecode for bool {
266    fn from_noun(noun: &Noun) -> Option<Self> {
267        let Noun::Atom(a) = noun else {
268            return None;
269        };
270        if a == &UBig::from(0u64) {
271            Some(true)
272        } else if a == &UBig::from(1u64) {
273            Some(false)
274        } else {
275            None
276        }
277    }
278}
279
280impl<T: NounEncode> NounEncode for Option<T> {
281    fn to_noun(&self) -> Noun {
282        match self {
283            None => atom(0),
284            Some(value) => (0, value.to_noun()).to_noun(),
285        }
286    }
287}
288
289impl<T: NounDecode> NounDecode for Option<T> {
290    fn from_noun(noun: &Noun) -> Option<Self> {
291        match noun {
292            Noun::Cell(x, v) if **x == atom(0) => Some(Some(T::from_noun(v)?)),
293            Noun::Atom(x) if x.is_zero() => Some(None),
294            _ => None,
295        }
296    }
297}
298
299macro_rules! impl_nounable_for_tuple {
300    ($T0:ident => $i0:ident) => {};
301    ($T:ident => $t:ident $( $U:ident => $u:ident )+) => {
302        impl<$T: NounEncode, $($U: NounEncode),*> NounEncode for ($T, $($U),*) {
303            fn to_noun(&self) -> Noun {
304                let ($t, $($u),*) = self;
305                cons($t.to_noun(), ($($u),*).to_noun())
306            }
307        }
308
309        impl<$T: NounDecode, $($U: NounDecode),*> NounDecode for ($T, $($U),*) {
310            fn from_noun(noun: &Noun) -> Option<($T, $($U),*)> {
311                let Noun::Cell(a, b) = noun else {
312                    return None;
313                };
314                let a = <$T>::from_noun(a)?;
315                #[allow(unused_parens)]
316                let ($($u),*) = <($($U),*)>::from_noun(b)?;
317                Some((a, $($u),*))
318            }
319        }
320
321        impl_nounable_for_tuple!($($U => $u)*);
322    };
323}
324
325impl_nounable_for_tuple!(
326    A => a
327    B => b
328    C => c
329    D => d
330    E => e
331    F => f
332    G => g
333    H => h
334    I => i
335    J => j
336    K => k
337);
338
339impl<T: NounEncode, const N: usize> NounEncode for [T; N] {
340    fn to_noun(&self) -> Noun {
341        match self.split_last() {
342            None => unreachable!(),
343            Some((last, rest)) => {
344                let mut acc = last.to_noun();
345                for item in rest.iter().rev() {
346                    acc = cons(item.to_noun(), acc);
347                }
348                acc
349            }
350        }
351    }
352}
353
354impl<T: NounDecode, const N: usize> NounDecode for [T; N] {
355    fn from_noun(mut noun: &Noun) -> Option<Self> {
356        let mut ret = [(); N].map(|_| MaybeUninit::<T>::uninit());
357        for item in ret.iter_mut().take(N) {
358            let Noun::Cell(a, b) = noun else {
359                return None;
360            };
361            *item = MaybeUninit::<T>::new(T::from_noun(a)?);
362            noun = b;
363        }
364
365        // SAFETY: all elements have been initialized in the loop
366        Some(unsafe { ret.map(|v| v.assume_init()) })
367    }
368}
369
370// TODO: always append ~ at the end
371impl<T: NounEncode> NounEncode for &[T] {
372    fn to_noun(&self) -> Noun {
373        match self.split_last() {
374            None => atom(0),
375            Some((last, rest)) => {
376                let mut acc = last.to_noun();
377                for item in rest.iter().rev() {
378                    acc = cons(item.to_noun(), acc);
379                }
380                acc
381            }
382        }
383    }
384}
385
386impl<T: NounEncode> NounEncode for Vec<T> {
387    fn to_noun(&self) -> Noun {
388        let mut acc = atom(0);
389        for item in self.iter().rev() {
390            acc = cons(item.to_noun(), acc);
391        }
392        acc
393    }
394}
395
396impl<T: NounDecode> NounDecode for Vec<T> {
397    fn from_noun(mut noun: &Noun) -> Option<Self> {
398        let mut ret = vec![];
399        loop {
400            match noun {
401                Noun::Cell(a, b) => {
402                    ret.push(T::from_noun(a)?);
403                    noun = b;
404                }
405                Noun::Atom(v) => {
406                    if v.is_zero() {
407                        return Some(ret);
408                    } else {
409                        return None;
410                    }
411                }
412            }
413        }
414    }
415}
416
417impl NounEncode for &str {
418    fn to_noun(&self) -> Noun {
419        Noun::Atom(UBig::from_le_bytes(self.as_bytes()))
420    }
421}
422
423impl NounEncode for String {
424    fn to_noun(&self) -> Noun {
425        self.as_str().to_noun()
426    }
427}
428
429impl NounDecode for String {
430    fn from_noun(noun: &Noun) -> Option<Self> {
431        let Noun::Atom(a) = noun else {
432            return None;
433        };
434        String::from_utf8(a.to_le_bytes()).ok()
435    }
436}
437
438pub fn jam(noun: Noun) -> Vec<u8> {
439    fn met0_u64_to_usize(value: u64) -> usize {
440        (u64::BITS - value.leading_zeros()) as usize
441    }
442
443    fn met0_atom(atom: &UBig) -> usize {
444        atom.bit_len()
445    }
446
447    fn mat_backref(buffer: &mut BitVec<u8, Lsb0>, backref: usize) {
448        if backref == 0 {
449            buffer.push(true);
450            buffer.push(true);
451            buffer.push(true);
452            return;
453        }
454        let backref_sz = met0_u64_to_usize(backref as u64);
455        let backref_sz_sz = met0_u64_to_usize(backref_sz as u64);
456        buffer.push(true);
457        buffer.push(true);
458        let buffer_len = buffer.len();
459        buffer.resize(buffer_len + backref_sz_sz, false);
460        buffer.push(true);
461        let size_bits = BitSlice::<usize, Lsb0>::from_element(&backref_sz);
462        buffer.extend_from_bitslice(&size_bits[..backref_sz_sz - 1]);
463        let backref_bits = BitSlice::<usize, Lsb0>::from_element(&backref);
464        buffer.extend_from_bitslice(&backref_bits[..backref_sz]);
465    }
466
467    fn mat_atom(buffer: &mut BitVec<u8, Lsb0>, atom: &UBig) {
468        if atom.is_zero() {
469            buffer.push(false);
470            buffer.push(true);
471            return;
472        }
473        let atom_sz = met0_atom(atom);
474        let atom_sz_sz = met0_u64_to_usize(atom_sz as u64);
475        buffer.push(false);
476        let buffer_len = buffer.len();
477        buffer.resize(buffer_len + atom_sz_sz, false);
478        buffer.push(true);
479        let size_bits = BitSlice::<usize, Lsb0>::from_element(&atom_sz);
480        buffer.extend_from_bitslice(&size_bits[..atom_sz_sz - 1]);
481        let atom_bytes = atom.to_le_bytes();
482        let atom_bits = BitSlice::<u8, Lsb0>::from_slice(&atom_bytes);
483        buffer.extend_from_bitslice(&atom_bits[..atom_sz]);
484    }
485
486    fn find_backref(backrefs: &[(Noun, usize)], target: &Noun) -> Option<usize> {
487        backrefs
488            .iter()
489            .find(|(noun, _)| noun == target)
490            .map(|(_, offset)| *offset)
491    }
492
493    let mut backrefs: Vec<(Noun, usize)> = Vec::new();
494    let mut stack = Vec::new();
495    stack.push(noun);
496    let mut buffer = BitVec::<u8, Lsb0>::new();
497
498    while let Some(current) = stack.pop() {
499        if let Some(backref) = find_backref(&backrefs, &current) {
500            match &current {
501                Noun::Atom(atom) => {
502                    if met0_u64_to_usize(backref as u64) < met0_atom(atom) {
503                        mat_backref(&mut buffer, backref);
504                    } else {
505                        mat_atom(&mut buffer, atom);
506                    }
507                }
508                Noun::Cell(_, _) => {
509                    mat_backref(&mut buffer, backref);
510                }
511            }
512        } else {
513            let offset = buffer.len();
514            backrefs.push((current.clone(), offset));
515            match current {
516                Noun::Atom(atom) => {
517                    mat_atom(&mut buffer, &atom);
518                }
519                Noun::Cell(left, right) => {
520                    buffer.push(true);
521                    buffer.push(false);
522                    stack.push(*right);
523                    stack.push(*left);
524                }
525            }
526        }
527    }
528
529    buffer.into_vec()
530}
531
532pub fn cue(bytes: &[u8]) -> Option<Noun> {
533    cue_bitslice(BitSlice::from_slice(bytes))
534}
535
536pub fn cue_bitslice(buffer: &BitSlice<u8, Lsb0>) -> Option<Noun> {
537    #[derive(Copy, Clone)]
538    enum CueStackEntry {
539        DestinationPointer(*mut Noun),
540        BackRef(u64, *mut Noun),
541    }
542
543    pub fn next_up_to_n_bits<'a>(
544        cursor: &mut usize,
545        slice: &'a BitSlice<u8, Lsb0>,
546        n: usize,
547    ) -> &'a BitSlice<u8, Lsb0> {
548        let res = if (slice).len() >= *cursor + n {
549            &slice[*cursor..*cursor + n]
550        } else if slice.len() > *cursor {
551            &slice[*cursor..]
552        } else {
553            BitSlice::<u8, Lsb0>::empty()
554        };
555        *cursor += n;
556        res
557    }
558
559    pub fn rest_bits(cursor: usize, slice: &BitSlice<u8, Lsb0>) -> &BitSlice<u8, Lsb0> {
560        if slice.len() > cursor {
561            &slice[cursor..]
562        } else {
563            BitSlice::<u8, Lsb0>::empty()
564        }
565    }
566
567    fn get_size(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<usize> {
568        let buff_at_cursor = rest_bits(*cursor, buffer);
569        let bitsize = buff_at_cursor.first_one()?;
570        if bitsize == 0 {
571            *cursor += 1;
572            Some(0)
573        } else {
574            let mut size = [0u8; 8];
575            *cursor += bitsize + 1;
576            let size_bits = next_up_to_n_bits(cursor, buffer, bitsize - 1);
577            BitSlice::from_slice_mut(&mut size)[0..bitsize - 1].copy_from_bitslice(size_bits);
578            Some((u64::from_le_bytes(size) as usize) + (1 << (bitsize - 1)))
579        }
580    }
581
582    fn rub_backref(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<u64> {
583        // TODO: What's size here usually?
584        let size = get_size(cursor, buffer)?;
585        if size == 0 {
586            Some(0)
587        } else if size <= 64 {
588            // TODO: Size <= 64, so we can fit the backref in a direct atom?
589            let mut backref = [0u8; 8];
590            BitSlice::from_slice_mut(&mut backref)[0..size]
591                .copy_from_bitslice(&buffer[*cursor..*cursor + size]);
592            *cursor += size;
593            Some(u64::from_le_bytes(backref))
594        } else {
595            None
596        }
597    }
598
599    fn rub_atom(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<UBig> {
600        let size = get_size(cursor, buffer)?;
601        let bits = next_up_to_n_bits(cursor, buffer, size);
602        if size == 0 {
603            Some(UBig::from(0u64))
604        } else if size < 64 {
605            // Fits in a direct atom
606            let mut direct_raw = [0u8; 8];
607            BitSlice::from_slice_mut(&mut direct_raw)[0..bits.len()].copy_from_bitslice(bits);
608            Some(UBig::from(u64::from_le_bytes(direct_raw)))
609        } else {
610            // Need an indirect atom
611            let wordsize = (size + 63) >> 6;
612            let mut bytes = vec![0u8; wordsize * 8];
613            BitSlice::from_slice_mut(&mut bytes).copy_from_bitslice(bits);
614            Some(UBig::from_le_bytes(&bytes))
615        }
616    }
617
618    pub fn next_bit(cursor: &mut usize, slice: &BitSlice<u8, Lsb0>) -> bool {
619        if (*slice).len() > *cursor {
620            let res = slice[*cursor];
621            *cursor += 1;
622            res
623        } else {
624            false
625        }
626    }
627
628    let mut backref_map = BTreeMap::<u64, *mut Noun>::new();
629    let mut result = atom(0);
630    let mut cursor = 0;
631
632    let mut cue_stack = vec![];
633
634    cue_stack.push(CueStackEntry::DestinationPointer(&mut result as *mut Noun));
635
636    while let Some(stack_entry) = cue_stack.pop() {
637        unsafe {
638            // Capture the destination pointer and pop it off the stack
639            match stack_entry {
640                CueStackEntry::DestinationPointer(dest_ptr) => {
641                    // 1 bit
642                    if next_bit(&mut cursor, buffer) {
643                        // 11 tag: backref
644                        if next_bit(&mut cursor, buffer) {
645                            let backref = rub_backref(&mut cursor, buffer)?;
646                            *dest_ptr = (**backref_map.get(&backref)?).clone();
647                        } else {
648                            // 10 tag: cell
649                            let mut head = Box::new(atom(0));
650                            let head_ptr = (&mut *head) as *mut _;
651                            let mut tail = Box::new(atom(0));
652                            let tail_ptr = (&mut *tail) as *mut _;
653                            *dest_ptr = Noun::Cell(head, tail);
654                            let backref = (cursor - 2) as u64;
655                            backref_map.insert(backref, dest_ptr);
656                            cue_stack.push(CueStackEntry::BackRef(cursor as u64 - 2, dest_ptr));
657                            cue_stack.push(CueStackEntry::DestinationPointer(tail_ptr));
658                            cue_stack.push(CueStackEntry::DestinationPointer(head_ptr));
659                        }
660                    } else {
661                        // 0 tag: atom
662                        let backref: u64 = (cursor - 1) as u64;
663                        *dest_ptr = Noun::Atom(rub_atom(&mut cursor, buffer)?);
664                        backref_map.insert(backref, dest_ptr);
665                    }
666                }
667                CueStackEntry::BackRef(backref, noun_ptr) => {
668                    backref_map.insert(backref, noun_ptr);
669                }
670            }
671        }
672    }
673
674    Some(result)
675}