Skip to main content

iris_ztd/
noun.rs

1use crate::belt::based_check;
2use crate::crypto::cheetah::F6lt;
3#[cfg(feature = "wasm")]
4use alloc::format;
5#[cfg(feature = "wasm")]
6use alloc::string::ToString;
7use alloc::{
8    boxed::Box, collections::btree_map::BTreeMap, string::String, sync::Arc, vec, vec::Vec,
9};
10use bitvec::prelude::{BitSlice, Lsb0};
11use ibig::UBig;
12use num_traits::Zero;
13use serde::de::{Error as DeError, SeqAccess, Visitor};
14use serde::{ser::SerializeSeq, Serialize, Serializer};
15use serde::{Deserialize, Deserializer};
16
17use crate::{belt::Belt, crypto::cheetah::CheetahPoint, Digest};
18
19/// A transparent wrapper that encodes as a zero atom if the value is `None`.
20#[repr(transparent)]
21#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
22pub struct Zeroable<T>(pub Option<T>);
23
24impl<T> core::ops::Deref for Zeroable<T> {
25    type Target = Option<T>;
26
27    fn deref(&self) -> &Self::Target {
28        &self.0
29    }
30}
31
32impl<T> core::ops::DerefMut for Zeroable<T> {
33    fn deref_mut(&mut self) -> &mut Self::Target {
34        &mut self.0
35    }
36}
37
38pub fn noun_serialize<T: NounEncode, S>(v: &T, serializer: S) -> Result<S::Ok, S::Error>
39where
40    S: Serializer,
41{
42    v.to_noun().serialize(serializer)
43}
44
45pub fn noun_deserialize<'de, T: NounDecode, D>(deserializer: D) -> Result<T, D::Error>
46where
47    D: Deserializer<'de>,
48{
49    let r = Noun::deserialize(deserializer)?;
50    T::from_noun(&r).ok_or_else(|| DeError::custom("unable to parse noun"))
51}
52
53const fn mug(mut x: u64) -> u64 {
54    x = (x ^ (x >> 30)).wrapping_mul(0xbf58476d1ce4e5b9u64);
55    x = (x ^ (x >> 27)).wrapping_mul(0x94d049bb133111ebu64);
56    x ^ (x >> 31)
57}
58
59const fn mug_bytes(b: &[u8]) -> u64 {
60    let mut ret = 0u64;
61
62    let mut i = 0;
63
64    while i < b.len() {
65        ret = mug(ret.wrapping_add(b[i] as u64));
66        i += 1;
67    }
68
69    mug(ret)
70}
71
72fn mug_noun(noun: &Noun) -> u32 {
73    match noun {
74        Noun::Atom(atom) => mug(mug_bytes(&atom.to_le_bytes())) as u32,
75        Noun::Cell(left, right) => mug(left.0.mug as u64 | ((right.0.mug as u64) << 32)) as u32,
76    }
77}
78
79fn weight_noun(noun: &Noun) -> u32 {
80    match noun {
81        Noun::Atom(_) => 1,
82        Noun::Cell(left, right) => 1 + left.0.weight + right.0.weight,
83    }
84}
85
86#[derive(Clone, Debug)]
87struct HashNounContents {
88    noun: Noun,
89    mug: u32,
90    weight: u32,
91}
92
93impl From<Noun> for HashNounContents {
94    fn from(noun: Noun) -> Self {
95        Self {
96            mug: mug_noun(&noun),
97            weight: weight_noun(&noun),
98            noun,
99        }
100    }
101}
102
103#[derive(Clone, Debug)]
104pub struct HashNoun(Arc<HashNounContents>);
105
106impl From<Noun> for HashNoun {
107    fn from(noun: Noun) -> Self {
108        Self(Arc::new(noun.into()))
109    }
110}
111
112impl core::ops::Deref for HashNoun {
113    type Target = Noun;
114
115    fn deref(&self) -> &Self::Target {
116        &self.0.noun
117    }
118}
119
120/// Nock-native data structure
121///
122/// A Noun is an Atom or a Cell.
123///
124/// A Cell is a pair of Nouns.
125///
126/// An Atom is a natural number.
127///
128/// Specific to iris, serialized atoms are encoded as little-endian hex strings.
129#[derive(Clone, Debug)]
130#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
131#[cfg_attr(
132    feature = "wasm",
133    tsify(into_wasm_abi, from_wasm_abi, type = "string | [Noun]")
134)]
135pub enum Noun {
136    Atom(UBig),
137    Cell(HashNoun, HashNoun),
138}
139
140impl PartialEq for Noun {
141    fn eq(&self, other: &Self) -> bool {
142        match (self, other) {
143            (Noun::Atom(a), Noun::Atom(b)) => a == b,
144            (Noun::Cell(a1, b1), Noun::Cell(a2, b2)) => {
145                (Arc::as_ptr(&a1.0) == Arc::as_ptr(&a2.0)
146                    && Arc::as_ptr(&b1.0) == Arc::as_ptr(&b2.0))
147                    || (a1.0.mug == a2.0.mug
148                        && b1.0.mug == b2.0.mug
149                        && a1.0.weight == a2.0.weight
150                        && b1.0.weight == b2.0.weight
151                        && a1.0.noun == a2.0.noun
152                        && b1.0.noun == b2.0.noun)
153            }
154            _ => false,
155        }
156    }
157}
158
159impl Eq for Noun {}
160
161impl core::fmt::Display for Noun {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        struct AutoCons<'a>(&'a Noun);
164        impl<'a> core::fmt::Display for AutoCons<'a> {
165            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166                match self.0 {
167                    Noun::Atom(a) => write!(f, "{}", a),
168                    Noun::Cell(head, tail) => match &**tail {
169                        Noun::Cell(_, _) => write!(f, "{} {}", AutoCons(head), AutoCons(tail)),
170                        Noun::Atom(a) if a.is_zero() => AutoCons(head).fmt(f),
171                        Noun::Atom(_) => write!(f, "{} . {}", AutoCons(head), AutoCons(tail)),
172                    },
173                }
174            }
175        }
176
177        match self {
178            Noun::Atom(a) => write!(f, "{}", a),
179            Noun::Cell(_, _) => write!(f, "[{}]", AutoCons(self)),
180        }
181    }
182}
183
184impl Serialize for Noun {
185    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186    where
187        S: Serializer,
188    {
189        match self {
190            Self::Atom(v) => serializer.serialize_str(&alloc::format!("{v:x}")),
191            Self::Cell(a, b) => {
192                let mut seq = serializer.serialize_seq(None)?;
193
194                seq.serialize_element(&**a)?;
195
196                let mut b = b.clone();
197
198                while let Self::Cell(left, right) = &*b {
199                    seq.serialize_element(&**left)?;
200                    b = right.clone();
201                }
202
203                seq.serialize_element(&*b)?;
204
205                seq.end()
206            }
207        }
208    }
209}
210
211impl<'de> Deserialize<'de> for Noun {
212    fn deserialize<D>(de: D) -> Result<Self, D::Error>
213    where
214        D: Deserializer<'de>,
215    {
216        struct V;
217
218        impl<'de> Visitor<'de> for V {
219            type Value = Noun;
220
221            fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
222                f.write_str("atom or cell")
223            }
224
225            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
226            where
227                E: DeError,
228            {
229                let n = UBig::from_str_radix(s, 16).map_err(E::custom)?;
230                Ok(Noun::Atom(n))
231            }
232
233            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
234            where
235                A: SeqAccess<'de>,
236            {
237                let mut stack = Vec::new();
238
239                while let Some(noun) = seq.next_element::<Noun>()? {
240                    stack.push(noun);
241                }
242
243                if stack.len() < 2 {
244                    return Err(DeError::custom("expected at least 2 elements"));
245                }
246
247                let mut top = stack.pop().ok_or_else(|| DeError::custom("empty cell"))?;
248
249                while let Some(noun) = stack.pop() {
250                    top = Noun::Cell(noun.into(), top.into());
251                }
252
253                Ok(top)
254            }
255        }
256
257        de.deserialize_any(V)
258    }
259}
260
261pub trait NounCode: NounEncode + NounDecode {}
262impl<T: NounEncode + NounDecode> NounCode for T {}
263
264pub trait NounEncode {
265    fn to_noun(&self) -> Noun;
266}
267
268pub trait NounDecode: Sized {
269    fn from_noun(noun: &Noun) -> Option<Self>;
270}
271
272fn atom(value: u64) -> Noun {
273    Noun::Atom(UBig::from(value))
274}
275
276fn cons(left: Noun, right: Noun) -> Noun {
277    Noun::Cell(left.into(), right.into())
278}
279
280impl<T: NounEncode + ?Sized> NounEncode for &T {
281    fn to_noun(&self) -> Noun {
282        (**self).to_noun()
283    }
284}
285
286impl<T: NounEncode + ?Sized> NounEncode for Arc<T> {
287    fn to_noun(&self) -> Noun {
288        (**self).to_noun()
289    }
290}
291
292impl<T: NounDecode> NounDecode for Arc<T> {
293    fn from_noun(noun: &Noun) -> Option<Self> {
294        Some(Arc::new(T::from_noun(noun)?))
295    }
296}
297
298impl<T: NounEncode + ?Sized> NounEncode for Box<T> {
299    fn to_noun(&self) -> Noun {
300        (**self).to_noun()
301    }
302}
303
304impl<T: NounDecode> NounDecode for Box<T> {
305    fn from_noun(noun: &Noun) -> Option<Self> {
306        Some(Box::new(T::from_noun(noun)?))
307    }
308}
309
310impl NounEncode for Noun {
311    fn to_noun(&self) -> Noun {
312        self.clone()
313    }
314}
315
316impl NounDecode for Noun {
317    fn from_noun(noun: &Noun) -> Option<Self> {
318        Some(noun.clone())
319    }
320}
321
322impl NounEncode for () {
323    fn to_noun(&self) -> Noun {
324        atom(0)
325    }
326}
327
328impl NounDecode for () {
329    fn from_noun(noun: &Noun) -> Option<Self> {
330        if *noun == atom(0) {
331            Some(())
332        } else {
333            None
334        }
335    }
336}
337
338impl NounEncode for Belt {
339    fn to_noun(&self) -> Noun {
340        atom(self.0)
341    }
342}
343
344impl NounDecode for Belt {
345    fn from_noun(noun: &Noun) -> Option<Self> {
346        let Noun::Atom(a) = noun else {
347            return None;
348        };
349        let v = u64::try_from(a).ok()?;
350        if based_check(v) {
351            Some(Belt(v))
352        } else {
353            None
354        }
355    }
356}
357
358impl NounEncode for Digest {
359    fn to_noun(&self) -> Noun {
360        self.0.to_noun()
361    }
362}
363
364impl NounDecode for Digest {
365    fn from_noun(noun: &Noun) -> Option<Self> {
366        Some(Digest(<[Belt; 5]>::from_noun(noun)?))
367    }
368}
369
370impl NounEncode for CheetahPoint {
371    fn to_noun(&self) -> Noun {
372        (self.x.0, self.y.0, self.inf).to_noun()
373    }
374}
375
376impl NounDecode for CheetahPoint {
377    fn from_noun(noun: &Noun) -> Option<Self> {
378        let (x, y, inf) = NounDecode::from_noun(noun)?;
379        Some(Self {
380            x: F6lt(x),
381            y: F6lt(y),
382            inf,
383        })
384    }
385}
386
387macro_rules! impl_nounable_for_int {
388    ($($ty:ty),* $(,)?) => {
389        $(
390            impl NounEncode for $ty {
391                fn to_noun(&self) -> Noun {
392                    atom(*self as u64)
393                }
394            }
395
396            impl NounDecode for $ty {
397                fn from_noun(noun: &Noun) -> Option<$ty> {
398                    let Noun::Atom(a) = noun else {
399                        return None;
400                    };
401                    <$ty>::try_from(a).ok()
402                }
403            }
404        )*
405    };
406}
407
408impl_nounable_for_int!(i32, i64, isize, u32, u64, usize);
409
410impl NounEncode for bool {
411    fn to_noun(&self) -> Noun {
412        atom(if *self { 0 } else { 1 })
413    }
414}
415
416impl NounDecode for bool {
417    fn from_noun(noun: &Noun) -> Option<Self> {
418        let Noun::Atom(a) = noun else {
419            return None;
420        };
421        if a == &UBig::from(0u64) {
422            Some(true)
423        } else if a == &UBig::from(1u64) {
424            Some(false)
425        } else {
426            None
427        }
428    }
429}
430
431impl<T: NounEncode> NounEncode for Option<T> {
432    fn to_noun(&self) -> Noun {
433        match self {
434            None => atom(0),
435            Some(value) => (0, value.to_noun()).to_noun(),
436        }
437    }
438}
439
440impl<T: NounDecode> NounDecode for Option<T> {
441    fn from_noun(noun: &Noun) -> Option<Self> {
442        match noun {
443            Noun::Cell(x, v) if **x == atom(0) => Some(Some(T::from_noun(v)?)),
444            Noun::Atom(x) if x.is_zero() => Some(None),
445            _ => None,
446        }
447    }
448}
449
450impl<T: NounEncode> NounEncode for Zeroable<T> {
451    fn to_noun(&self) -> Noun {
452        match &self.0 {
453            None => atom(0),
454            Some(value) => value.to_noun(),
455        }
456    }
457}
458
459impl<T: NounDecode> NounDecode for Zeroable<T> {
460    fn from_noun(noun: &Noun) -> Option<Self> {
461        match noun {
462            Noun::Atom(x) if x.is_zero() => Some(Zeroable(None)),
463            Noun::Atom(_) => None,
464            v => Some(Zeroable(Some(T::from_noun(v)?))),
465        }
466    }
467}
468
469macro_rules! impl_nounable_for_tuple {
470    ($T0:ident => $i0:ident) => {};
471    ($T:ident => $t:ident $( $U:ident => $u:ident )+) => {
472        impl<$T: NounEncode, $($U: NounEncode),*> NounEncode for ($T, $($U),*) {
473            fn to_noun(&self) -> Noun {
474                let ($t, $($u),*) = self;
475                cons($t.to_noun(), ($($u),*).to_noun())
476            }
477        }
478
479        impl<$T: NounDecode, $($U: NounDecode),*> NounDecode for ($T, $($U),*) {
480            fn from_noun(noun: &Noun) -> Option<($T, $($U),*)> {
481                let Noun::Cell(a, b) = noun else {
482                    return None;
483                };
484                let a = <$T>::from_noun(a)?;
485                #[allow(unused_parens)]
486                let ($($u),*) = <($($U),*)>::from_noun(b)?;
487                Some((a, $($u),*))
488            }
489        }
490
491        impl_nounable_for_tuple!($($U => $u)*);
492    };
493}
494
495impl_nounable_for_tuple!(
496    A => a
497    B => b
498    C => c
499    D => d
500    E => e
501    F => f
502    G => g
503    H => h
504    I => i
505    J => j
506    K => k
507    L => l
508    M => m
509    N => n
510    O => o
511    P => p
512    Q => q
513    R => r
514    S => s
515    T => t
516);
517
518impl<T: NounEncode, const N: usize> NounEncode for [T; N] {
519    fn to_noun(&self) -> Noun {
520        match self.split_last() {
521            None => self[0].to_noun(),
522            Some((last, rest)) => {
523                let mut acc = last.to_noun();
524                for item in rest.iter().rev() {
525                    acc = cons(item.to_noun(), acc);
526                }
527                acc
528            }
529        }
530    }
531}
532
533impl<T: NounDecode, const N: usize> NounDecode for [T; N] {
534    fn from_noun(mut noun: &Noun) -> Option<Self> {
535        let mut ret: [Option<T>; N] = [(); N].map(|_| None);
536        for (i, item) in ret.iter_mut().enumerate() {
537            let decode = match noun {
538                Noun::Cell(a, b) => {
539                    noun = b;
540                    a
541                }
542                v if i == N - 1 => v,
543                _ => return None,
544            };
545            *item = Some(T::from_noun(decode)?);
546        }
547
548        Some(ret.map(|v| v.unwrap()))
549    }
550}
551
552impl<T: NounEncode> NounEncode for &[T] {
553    fn to_noun(&self) -> Noun {
554        match self.split_last() {
555            None => atom(0),
556            Some((last, rest)) => {
557                let mut acc = last.to_noun();
558                for item in rest.iter().rev() {
559                    acc = cons(item.to_noun(), acc);
560                }
561                acc
562            }
563        }
564    }
565}
566
567impl<T: NounEncode> NounEncode for Vec<T> {
568    fn to_noun(&self) -> Noun {
569        let mut acc = atom(0);
570        for item in self.iter().rev() {
571            acc = cons(item.to_noun(), acc);
572        }
573        acc
574    }
575}
576
577impl<T: NounDecode> NounDecode for Vec<T> {
578    fn from_noun(mut noun: &Noun) -> Option<Self> {
579        let mut ret = vec![];
580        loop {
581            match noun {
582                Noun::Cell(a, b) => {
583                    ret.push(T::from_noun(a)?);
584                    noun = b;
585                }
586                Noun::Atom(v) => {
587                    if v.is_zero() {
588                        return Some(ret);
589                    } else {
590                        return None;
591                    }
592                }
593            }
594        }
595    }
596}
597
598impl NounEncode for &str {
599    fn to_noun(&self) -> Noun {
600        Noun::Atom(UBig::from_le_bytes(self.as_bytes()))
601    }
602}
603
604impl NounEncode for String {
605    fn to_noun(&self) -> Noun {
606        self.as_str().to_noun()
607    }
608}
609
610impl NounDecode for String {
611    fn from_noun(noun: &Noun) -> Option<Self> {
612        let Noun::Atom(a) = noun else {
613            return None;
614        };
615        String::from_utf8(a.to_le_bytes()).ok()
616    }
617}
618
619/// Jam a noun into bytes vec
620pub fn jam(noun: Noun) -> Vec<u8> {
621    fn met0_u64_to_usize(value: u64) -> usize {
622        (u64::BITS - value.leading_zeros()) as usize
623    }
624
625    fn met0_atom(atom: &UBig) -> usize {
626        atom.bit_len()
627    }
628
629    fn mat_backref(writer: &mut BitWriter, backref: usize) {
630        if backref == 0 {
631            writer.write_bits_from_value(0b111, 3); // 1 1 1
632            return;
633        }
634        let backref_sz = met0_u64_to_usize(backref as u64);
635        let backref_sz_sz = met0_u64_to_usize(backref_sz as u64);
636        // backref tag 1 1
637        writer.write_bit(true);
638        writer.write_bit(true);
639        // write backref_sz_sz zeros
640        writer.write_zeros(backref_sz_sz);
641        // delimiter 1
642        writer.write_bit(true);
643        // write backref_sz_sz-1 bits of backref_sz (LSB first)
644        writer.write_bits_from_value(backref_sz, backref_sz_sz - 1);
645        // write backref bits (backref_sz bits)
646        writer.write_bits_from_value(backref, backref_sz);
647    }
648
649    fn mat_atom(writer: &mut BitWriter, atom: &UBig) {
650        if atom.is_zero() {
651            writer.write_bits_from_value(0b10, 2); // 0 1
652            return;
653        }
654        let atom_sz = met0_atom(atom);
655        let atom_sz_sz = met0_u64_to_usize(atom_sz as u64);
656        writer.write_bit(false); // atom tag 0
657        writer.write_zeros(atom_sz_sz); // size zeros
658        writer.write_bit(true); // delimiter
659                                // write size bits (atom_sz_sz - 1)
660        writer.write_bits_from_value(atom_sz, atom_sz_sz - 1);
661        // write atom bits (little-endian order)
662        writer.write_bits_from_le_bytes(&atom.to_le_bytes(), atom_sz);
663    }
664
665    fn find_backref(
666        backrefs: &BTreeMap<(u32, u32), Vec<(Noun, usize)>>,
667        weight: u32,
668        mug: u32,
669        target: &Noun,
670    ) -> Option<usize> {
671        backrefs
672            .get(&(weight, mug))
673            .and_then(|vec| vec.iter().find(|(n, _)| n == target))
674            .map(|(_, offset)| *offset)
675    }
676
677    let mut backrefs: BTreeMap<(u32, u32), Vec<(Noun, usize)>> = BTreeMap::new();
678    let mut stack = Vec::new();
679    stack.push((weight_noun(&noun), mug_noun(&noun), noun));
680    let mut buffer = BitWriter::new();
681
682    while let Some((weight, mug, current)) = stack.pop() {
683        if let Some(backref) = find_backref(&backrefs, weight, mug, &current) {
684            match &current {
685                Noun::Atom(atom) => {
686                    if met0_u64_to_usize(backref as u64) < met0_atom(atom) {
687                        mat_backref(&mut buffer, backref);
688                    } else {
689                        mat_atom(&mut buffer, atom);
690                    }
691                }
692                Noun::Cell(_, _) => {
693                    mat_backref(&mut buffer, backref);
694                }
695            }
696        } else {
697            let offset = buffer.bit_len();
698            backrefs
699                .entry((weight, mug))
700                .or_default()
701                .push((current.clone(), offset));
702            match current {
703                Noun::Atom(atom) => {
704                    mat_atom(&mut buffer, &atom);
705                }
706                Noun::Cell(left, right) => {
707                    buffer.write_bit(true);
708                    buffer.write_bit(false);
709                    stack.push((right.0.weight, right.0.mug, (*right).clone()));
710                    stack.push((left.0.weight, left.0.mug, (*left).clone()));
711                }
712            }
713        }
714    }
715
716    buffer.into_vec()
717}
718
719/// Cue jammed bytes into Noun (see `jam`)
720pub fn cue(bytes: &[u8]) -> Option<Noun> {
721    cue_bitslice(BitSlice::from_slice(bytes))
722}
723
724pub fn cue_bitslice(buffer: &BitSlice<u8, Lsb0>) -> Option<Noun> {
725    #[derive(Copy, Clone)]
726    enum CueStackEntry {
727        DestinationPointer(*mut Noun),
728        BackRef(u64, *mut Noun),
729    }
730
731    pub fn next_up_to_n_bits<'a>(
732        cursor: &mut usize,
733        slice: &'a BitSlice<u8, Lsb0>,
734        n: usize,
735    ) -> &'a BitSlice<u8, Lsb0> {
736        let res = if (slice).len() >= *cursor + n {
737            &slice[*cursor..*cursor + n]
738        } else if slice.len() > *cursor {
739            &slice[*cursor..]
740        } else {
741            BitSlice::<u8, Lsb0>::empty()
742        };
743        *cursor += n;
744        res
745    }
746
747    pub fn rest_bits(cursor: usize, slice: &BitSlice<u8, Lsb0>) -> &BitSlice<u8, Lsb0> {
748        if slice.len() > cursor {
749            &slice[cursor..]
750        } else {
751            BitSlice::<u8, Lsb0>::empty()
752        }
753    }
754
755    fn get_size(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<usize> {
756        let buff_at_cursor = rest_bits(*cursor, buffer);
757        let bitsize = buff_at_cursor.first_one()?;
758        if bitsize == 0 {
759            *cursor += 1;
760            Some(0)
761        } else {
762            let mut size = [0u8; 8];
763            *cursor += bitsize + 1;
764            let size_bits = next_up_to_n_bits(cursor, buffer, bitsize - 1);
765            BitSlice::from_slice_mut(&mut size)[0..bitsize - 1].copy_from_bitslice(size_bits);
766            Some((u64::from_le_bytes(size) as usize) + (1 << (bitsize - 1)))
767        }
768    }
769
770    fn rub_backref(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<u64> {
771        // TODO: What's size here usually?
772        let size = get_size(cursor, buffer)?;
773        if size == 0 {
774            Some(0)
775        } else if size <= 64 {
776            // TODO: Size <= 64, so we can fit the backref in a direct atom?
777            let mut backref = [0u8; 8];
778            BitSlice::from_slice_mut(&mut backref)[0..size]
779                .copy_from_bitslice(&buffer[*cursor..*cursor + size]);
780            *cursor += size;
781            Some(u64::from_le_bytes(backref))
782        } else {
783            None
784        }
785    }
786
787    fn rub_atom(cursor: &mut usize, buffer: &BitSlice<u8, Lsb0>) -> Option<UBig> {
788        let size = get_size(cursor, buffer)?;
789        let bits = next_up_to_n_bits(cursor, buffer, size);
790        if size == 0 {
791            Some(UBig::from(0u64))
792        } else if size < 64 {
793            // Fits in a direct atom
794            let mut direct_raw = [0u8; 8];
795            BitSlice::from_slice_mut(&mut direct_raw)[0..bits.len()].copy_from_bitslice(bits);
796            Some(UBig::from(u64::from_le_bytes(direct_raw)))
797        } else {
798            // Need an indirect atom
799            let wordsize = (size + 63) >> 6;
800            let mut bytes = vec![0u8; wordsize * 8];
801            BitSlice::from_slice_mut(&mut bytes)[0..bits.len()].copy_from_bitslice(bits);
802            Some(UBig::from_le_bytes(&bytes))
803        }
804    }
805
806    pub fn next_bit(cursor: &mut usize, slice: &BitSlice<u8, Lsb0>) -> bool {
807        if (*slice).len() > *cursor {
808            let res = slice[*cursor];
809            *cursor += 1;
810            res
811        } else {
812            false
813        }
814    }
815
816    let mut backref_map = BTreeMap::<u64, *mut Noun>::new();
817    let mut result = atom(0);
818    let mut cursor = 0;
819
820    let mut cue_stack = vec![];
821
822    cue_stack.push(CueStackEntry::DestinationPointer(&mut result as *mut Noun));
823
824    while let Some(stack_entry) = cue_stack.pop() {
825        unsafe {
826            // Capture the destination pointer and pop it off the stack
827            match stack_entry {
828                CueStackEntry::DestinationPointer(dest_ptr) => {
829                    // 1 bit
830                    if next_bit(&mut cursor, buffer) {
831                        // 11 tag: backref
832                        if next_bit(&mut cursor, buffer) {
833                            let backref = rub_backref(&mut cursor, buffer)?;
834                            *dest_ptr = (**backref_map.get(&backref)?).clone();
835                        } else {
836                            // 10 tag: cell
837                            let head = HashNoun::from(atom(0));
838                            let head_ptr = Arc::as_ptr(&head.0) as *mut _;
839                            let tail = HashNoun::from(atom(0));
840                            let tail_ptr = Arc::as_ptr(&tail.0) as *mut _;
841                            *dest_ptr = Noun::Cell(head, tail);
842                            let backref = (cursor - 2) as u64;
843                            backref_map.insert(backref, dest_ptr);
844                            cue_stack.push(CueStackEntry::BackRef(cursor as u64 - 2, dest_ptr));
845                            cue_stack.push(CueStackEntry::DestinationPointer(tail_ptr));
846                            cue_stack.push(CueStackEntry::DestinationPointer(head_ptr));
847                        }
848                    } else {
849                        // 0 tag: atom
850                        let backref: u64 = (cursor - 1) as u64;
851                        *dest_ptr = Noun::Atom(rub_atom(&mut cursor, buffer)?);
852                        backref_map.insert(backref, dest_ptr);
853                    }
854                }
855                CueStackEntry::BackRef(backref, noun_ptr) => {
856                    backref_map.insert(backref, noun_ptr);
857                }
858            }
859        }
860    }
861
862    Some(result)
863}
864
865// Fast bit writer that appends bits LSB-first into an underlying Vec<u8>
866pub struct BitWriter {
867    buf: Vec<u8>,   // final byte buffer (little-endian bit order per byte)
868    acc: u8,        // in-progress byte accumulator
869    nbits: u8,      // number of bits currently stored in `acc` (0-7)
870    bit_len: usize, // total number of bits written so far
871}
872impl Default for BitWriter {
873    fn default() -> Self {
874        Self::new()
875    }
876}
877
878impl BitWriter {
879    #[inline]
880    pub fn new() -> Self {
881        BitWriter {
882            buf: Vec::with_capacity(1024),
883            acc: 0,
884            nbits: 0,
885            bit_len: 0,
886        }
887    }
888
889    #[inline]
890    pub fn bit_len(&self) -> usize {
891        self.bit_len
892    }
893
894    #[inline]
895    pub fn write_bit(&mut self, bit: bool) {
896        if bit {
897            self.acc |= 1 << self.nbits;
898        }
899        self.nbits += 1;
900        self.bit_len += 1;
901        if self.nbits == 8 {
902            self.flush_acc();
903        }
904    }
905
906    #[inline]
907    pub fn write_zeros(&mut self, count: usize) {
908        // produce `count` zero bits quickly
909        // Fill partial acc first
910        let mut remaining = count;
911        if self.nbits != 0 {
912            let space = 8 - self.nbits;
913            if remaining < space as usize {
914                // // just bump counters – acc already contains zeros in high bits
915                // self.nbits += remaining as u8;
916                // self.bit_len += remaining;
917                // return;
918                // keep the valid low bits we already had, clear the bits we are about to add
919                let mask = (1u16 << self.nbits) - 1; // e.g. nbits = 3  -> 0b00000111
920                self.acc &= mask as u8; // zero out bits [self.nbits .. 7]
921
922                // now bump the cursors exactly as before
923                self.nbits += remaining as u8;
924                self.bit_len += remaining;
925                return;
926            } else {
927                // fill acc with zeros and flush
928                // self.nbits = 8;
929                // self.bit_len += space as usize;
930                // remaining -= space as usize;
931                // zero-fill high bits we are about to claim
932                let mask = (1u16 << self.nbits) - 1; // keep the `nbits` low bits
933                self.acc &= mask as u8; // clear [self.nbits .. 7]
934
935                // now top-off the byte and flush
936                self.nbits = 8;
937                self.bit_len += space as usize;
938                remaining -= space as usize;
939                self.flush_acc();
940            }
941        }
942        // Now we are byte-aligned
943        let full_bytes = remaining / 8;
944        if full_bytes > 0 {
945            self.buf.extend(core::iter::repeat_n(0u8, full_bytes));
946            self.bit_len += full_bytes * 8;
947            remaining -= full_bytes * 8;
948        }
949        // Remaining < 8, leave in acc (which is zero)
950        self.nbits = remaining as u8;
951        self.acc = 0; // already zero
952        self.bit_len += remaining;
953    }
954
955    #[inline]
956    pub fn write_bits_from_value(&mut self, mut value: usize, count: usize) {
957        for _ in 0..count {
958            self.write_bit((value & 1) == 1);
959            value >>= 1;
960        }
961    }
962
963    #[inline]
964    pub fn write_bits_from_le_bytes(&mut self, bytes: &[u8], total_bits: usize) {
965        if total_bits == 0 {
966            return;
967        }
968
969        let full_bytes = total_bits / 8;
970        let rem_bits: usize = total_bits % 8;
971
972        if self.nbits == 0 {
973            // Aligned path: copy full bytes directly
974            if full_bytes > 0 {
975                self.buf.extend_from_slice(&bytes[..full_bytes]);
976                self.bit_len += full_bytes * 8;
977            }
978        } else if full_bytes > 0 {
979            // Unaligned path: merge each byte with current accumulator
980            let shift = self.nbits;
981            let mut carry = self.acc;
982            for &byte in &bytes[..full_bytes] {
983                let combined = carry | (byte << shift);
984                self.buf.push(combined);
985                self.bit_len += 8;
986                carry = byte >> (8 - shift);
987            }
988            self.acc = carry;
989            // note: nbits unchanged
990        }
991
992        // Handle remaining bits (<8) from the next byte
993        if rem_bits > 0 {
994            let src_byte = if full_bytes < bytes.len() {
995                bytes[full_bytes]
996            } else {
997                0
998            };
999            for i in 0..rem_bits {
1000                self.write_bit(((src_byte >> i) & 1) == 1);
1001            }
1002        }
1003        // Update bit_len to reflect the total number of bits written so far
1004        // This didn't work.
1005        // self.bit_len = self.buf.len() * 8 + self.nbits as usize;
1006    }
1007
1008    #[inline]
1009    pub fn flush_acc(&mut self) {
1010        if self.nbits == 0 {
1011            return;
1012        }
1013        self.buf.push(self.acc);
1014        self.acc = 0;
1015        self.nbits = 0;
1016    }
1017
1018    pub fn into_vec(mut self) -> Vec<u8> {
1019        if self.nbits > 0 {
1020            // Flush final partial byte (upper bits remain 0)
1021            self.flush_acc();
1022        }
1023        self.buf
1024    }
1025}