• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Chik-Network / klvm_rs / 16768635150

23 Jun 2025 03:12AM UTC coverage: 93.675% (-0.6%) from 94.298%
16768635150

push

github

Chik-Network
update 0.15.0

90 of 92 new or added lines in 3 files covered. (97.83%)

11 existing lines in 3 files now uncovered.

6206 of 6625 relevant lines covered (93.68%)

30948374.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.03
/src/allocator.rs
1
use crate::err_utils::err;
2
use crate::number::{number_from_u8, Number};
3
use crate::reduction::EvalErr;
4
use chik_bls::{G1Element, G2Element};
5
use std::borrow::Borrow;
6
use std::fmt;
7
use std::hash::Hash;
8
use std::hash::Hasher;
9
use std::ops::Deref;
10

11
#[cfg(feature = "allocator-debug")]
12
use rand::RngCore;
13

14
#[cfg(feature = "allocator-debug")]
15
use rand;
16

17
const MAX_NUM_ATOMS: usize = 62500000;
18
const MAX_NUM_PAIRS: usize = 62500000;
19
const NODE_PTR_IDX_BITS: u32 = 26;
20
const NODE_PTR_IDX_MASK: u32 = (1 << NODE_PTR_IDX_BITS) - 1;
21

22
#[cfg(feature = "allocator-debug")]
23
#[derive(Clone, Copy)]
24
struct AllocatorReference {
25
    fingerprint: u64,
26
    version: usize,
27
}
28

29
#[cfg(feature = "allocator-debug")]
30
#[derive(Clone, Copy)]
31
pub struct NodePtr(u32, AllocatorReference);
32

33
#[cfg(feature = "allocator-debug")]
34
impl Hash for NodePtr {
35
    fn hash<H: Hasher>(&self, state: &mut H) {
36
        self.0.hash(state);
37
    }
38
}
39

40
#[cfg(feature = "allocator-debug")]
41
impl PartialEq for NodePtr {
42
    fn eq(&self, other: &Self) -> bool {
43
        self.0.eq(&other.0)
44
    }
45
}
46

47
#[cfg(feature = "allocator-debug")]
48
impl Eq for NodePtr {}
49

50
#[cfg(not(feature = "allocator-debug"))]
51
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52
pub struct NodePtr(u32);
53

54
impl fmt::Debug for NodePtr {
UNCOV
55
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
56
        f.debug_tuple("NodePtr")
×
UNCOV
57
            .field(&self.object_type())
×
UNCOV
58
            .field(&self.index())
×
UNCOV
59
            .finish()
×
UNCOV
60
    }
×
61
}
62

63
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64
pub enum ObjectType {
65
    // The low bits form an index into the pair_vec
66
    Pair,
67
    // The low bits form an index into the atom_vec
68
    Bytes,
69
    // The low bits are the atom itself (unsigned integer, 26 bits)
70
    SmallAtom,
71
}
72

73
// The top 6 bits of the NodePtr indicate what type of object it is
74
impl NodePtr {
75
    pub const NIL: Self = Self::new(ObjectType::SmallAtom, 0);
76

77
    #[cfg(not(feature = "allocator-debug"))]
78
    const fn new(object_type: ObjectType, index: usize) -> Self {
1,790,161,620✔
79
        debug_assert!(index <= NODE_PTR_IDX_MASK as usize);
1,790,161,620✔
80
        NodePtr(((object_type as u32) << NODE_PTR_IDX_BITS) | (index as u32))
1,790,161,620✔
81
    }
1,790,161,620✔
82

83
    #[cfg(feature = "allocator-debug")]
84
    const fn new(object_type: ObjectType, index: usize) -> Self {
85
        debug_assert!(index <= NODE_PTR_IDX_MASK as usize);
86
        NodePtr(
87
            ((object_type as u32) << NODE_PTR_IDX_BITS) | (index as u32),
88
            AllocatorReference {
89
                fingerprint: u64::MAX,
90
                version: usize::MAX,
91
            },
92
        )
93
    }
94

95
    #[cfg(feature = "allocator-debug")]
96
    const fn new_debug(object_type: ObjectType, index: usize, ar: AllocatorReference) -> Self {
97
        debug_assert!(index <= NODE_PTR_IDX_MASK as usize);
98
        NodePtr(
99
            ((object_type as u32) << NODE_PTR_IDX_BITS) | (index as u32),
100
            ar,
101
        )
102
    }
103

104
    pub fn is_atom(self) -> bool {
×
105
        matches!(
×
106
            self.object_type(),
×
107
            ObjectType::Bytes | ObjectType::SmallAtom
108
        )
109
    }
×
110

111
    pub fn is_pair(self) -> bool {
21,757✔
112
        self.object_type() == ObjectType::Pair
21,757✔
113
    }
21,757✔
114

115
    /// This is an advanced API that exposes implementation details.
116
    /// Returns the internal representation of this node
117
    pub fn object_type(self) -> ObjectType {
2,147,483,647✔
118
        match self.0 >> NODE_PTR_IDX_BITS {
2,147,483,647✔
119
            0 => ObjectType::Pair,
2,147,483,647✔
120
            1 => ObjectType::Bytes,
91,949,109✔
121
            2 => ObjectType::SmallAtom,
2,147,483,647✔
122
            _ => unreachable!(),
1✔
123
        }
124
    }
2,147,483,647✔
125

126
    /// This is an advanced API that exposes implementation details.
127
    /// Returns a dense index of low numbers for the specific ObjectType
128
    pub fn index(self) -> u32 {
2,147,483,647✔
129
        self.0 & NODE_PTR_IDX_MASK
2,147,483,647✔
130
    }
2,147,483,647✔
131
}
132

133
impl Default for NodePtr {
134
    fn default() -> Self {
1✔
135
        Self::NIL
1✔
136
    }
1✔
137
}
138

139
#[derive(PartialEq, Debug)]
140
pub enum SExp {
141
    Atom,
142
    Pair(NodePtr, NodePtr),
143
}
144

145
#[derive(Clone, Copy, Debug)]
146
struct AtomBuf {
147
    start: u32,
148
    end: u32,
149
}
150

151
impl AtomBuf {
152
    pub fn len(&self) -> usize {
240✔
153
        (self.end - self.start) as usize
240✔
154
    }
240✔
155
}
156

157
#[derive(Clone, Copy, Debug)]
158
pub struct IntPair {
159
    first: NodePtr,
160
    rest: NodePtr,
161
}
162

163
// this represents a specific (former) state of an allocator. This can be used
164
// to restore an allocator to a previous state. It cannot be used to re-create
165
// the state from some other allocator.
166
pub struct Checkpoint {
167
    u8s: usize,
168
    pairs: usize,
169
    atoms: usize,
170
    ghost_atoms: usize,
171
    ghost_pairs: usize,
172
    ghost_heap: usize,
173
}
174

175
pub enum NodeVisitor<'a> {
176
    Buffer(&'a [u8]),
177
    U32(u32),
178
    Pair(NodePtr, NodePtr),
179
}
180

181
#[derive(Debug, Clone, Copy, Eq)]
182
pub enum Atom<'a> {
183
    Borrowed(&'a [u8]),
184
    U32([u8; 4], usize),
185
}
186

187
impl Hash for Atom<'_> {
188
    fn hash<H: Hasher>(&self, state: &mut H) {
×
189
        self.as_ref().hash(state)
×
190
    }
×
191
}
192

193
impl PartialEq for Atom<'_> {
194
    fn eq(&self, other: &Atom) -> bool {
×
195
        self.as_ref().eq(other.as_ref())
×
196
    }
×
197
}
198

199
impl AsRef<[u8]> for Atom<'_> {
200
    fn as_ref(&self) -> &[u8] {
28,442,216✔
201
        match self {
28,442,216✔
202
            Self::Borrowed(bytes) => bytes,
11,389,868✔
203
            Self::U32(bytes, len) => &bytes[4 - len..],
17,052,348✔
204
        }
205
    }
28,442,216✔
206
}
207

208
impl Deref for Atom<'_> {
209
    type Target = [u8];
210

211
    fn deref(&self) -> &Self::Target {
×
212
        self.as_ref()
×
213
    }
×
214
}
215

216
impl Borrow<[u8]> for Atom<'_> {
217
    fn borrow(&self) -> &[u8] {
368✔
218
        self.as_ref()
368✔
219
    }
368✔
220
}
221

222
#[derive(Debug)]
223
pub struct Allocator {
224
    // this is effectively a grow-only stack where atoms are allocated. Atoms
225
    // are immutable, so once they are created, they will stay around until the
226
    // program completes
227
    u8_vec: Vec<u8>,
228

229
    // storage for all pairs (positive indices)
230
    pair_vec: Vec<IntPair>,
231

232
    // storage for all atoms (negative indices).
233
    // node index -1 refers to index 0 in this vector, -2 refers to 1 and so
234
    // on.
235
    atom_vec: Vec<AtomBuf>,
236

237
    // the atom_vec may not grow past this
238
    heap_limit: usize,
239

240
    // the ghost counters are pretend atoms/pairs, that were optimized out. We
241
    // still account for them to not affect the limits of atoms and pairs. Those
242
    // limits must stay the same for consensus purpose.
243
    // For example, a "small atom", which is allocated in-place in the NodePtr.
244
    ghost_atoms: usize,
245
    ghost_pairs: usize,
246
    ghost_heap: usize,
247

248
    #[cfg(feature = "allocator-debug")]
249
    fingerprint: u64,
250

251
    // the number of atoms and pairs at different versions
252
    #[cfg(feature = "allocator-debug")]
253
    versions: Vec<(usize, usize)>,
254
}
255

256
impl Default for Allocator {
257
    fn default() -> Self {
×
258
        Self::new()
×
259
    }
×
260
}
261

262
pub fn fits_in_small_atom(v: &[u8]) -> Option<u32> {
274,474,443✔
263
    if !v.is_empty()
274,474,443✔
264
        && (v.len() > 4
274,474,398✔
265
        || (v.len() == 1 && v[0] == 0)
274,153,635✔
266
        // a 1-byte buffer of 0 is not the canonical representation of 0
267
        || (v[0] & 0x80) != 0
274,153,593✔
268
        // if the top bit is set, it's a negative number (i.e. not positive)
269
        || (v[0] == 0 && (v[1] & 0x80) == 0)
274,149,624✔
270
        // if the buffer is 4 bytes, the top byte can't use more than 2 bits.
271
        // otherwise the integer won't fit in 26 bits
272
        || (v.len() == 4 && v[0] > 0x03))
251,422,133✔
273
    {
274
        // if the top byte is a 0 but the top bit of the next byte is not set,
275
        // that's a redundant leading zero. i.e. not canonical representation
276
        None
23,052,672✔
277
    } else {
278
        let mut ret: u32 = 0;
251,421,771✔
279
        for b in v {
1,002,846,076✔
280
            ret <<= 8;
751,424,305✔
281
            ret |= *b as u32;
751,424,305✔
282
        }
751,424,305✔
283
        Some(ret)
251,421,771✔
284
    }
285
}
274,474,443✔
286

287
pub fn len_for_value(val: u32) -> usize {
909,678,323✔
288
    if val == 0 {
909,678,323✔
289
        0
466,030,498✔
290
    } else if val < 0x80 {
443,647,825✔
291
        1
288,501,791✔
292
    } else if val < 0x8000 {
155,146,034✔
293
        2
22,824,821✔
294
    } else if val < 0x800000 {
132,321,213✔
295
        3
54,368,327✔
296
    } else if val < 0x80000000 {
77,952,886✔
297
        4
77,952,882✔
298
    } else {
299
        5
4✔
300
    }
301
}
909,678,323✔
302

303
impl Allocator {
304
    pub fn new() -> Self {
5,440✔
305
        Self::new_limited(u32::MAX as usize)
5,440✔
306
    }
5,440✔
307

308
    pub fn new_limited(heap_limit: usize) -> Self {
5,442✔
309
        // we have a maximum of 4 GiB heap, because pointers are 32 bit unsigned
310
        assert!(heap_limit <= u32::MAX as usize);
5,442✔
311

312
        let mut r = Self {
5,442✔
313
            u8_vec: Vec::new(),
5,442✔
314
            pair_vec: Vec::new(),
5,442✔
315
            atom_vec: Vec::new(),
5,442✔
316
            // subtract 1 to compensate for the one() we used to allocate unconfitionally
5,442✔
317
            heap_limit: heap_limit - 1,
5,442✔
318
            // initialize this to 2 to behave as if we had allocated atoms for
5,442✔
319
            // nil() and one(), like we used to
5,442✔
320
            ghost_atoms: 2,
5,442✔
321
            ghost_pairs: 0,
5,442✔
322
            ghost_heap: 0,
5,442✔
323

5,442✔
324
            #[cfg(feature = "allocator-debug")]
5,442✔
325
            fingerprint: rand::thread_rng().next_u64(),
5,442✔
326

5,442✔
327
            #[cfg(feature = "allocator-debug")]
5,442✔
328
            versions: Vec::new(),
5,442✔
329
        };
5,442✔
330
        r.u8_vec.reserve(1024 * 1024);
5,442✔
331
        r.atom_vec.reserve(256);
5,442✔
332
        r.pair_vec.reserve(256);
5,442✔
333
        r
5,442✔
334
    }
5,442✔
335

336
    #[cfg(feature = "allocator-debug")]
337
    fn validate_node(&self, n: NodePtr) {
338
        if n.1.fingerprint == u64::MAX && n.1.version == usize::MAX {
339
            assert!(matches!(n.object_type(), ObjectType::SmallAtom));
340
            return;
341
        }
342

343
        assert_eq!(
344
            n.1.fingerprint, self.fingerprint,
345
            "using a NodePtr on the wrong Allocator"
346
        );
347
        // if n.1.version is equal to self.versions.len() it means no
348
        // restore_checkpoint() has been called since this NodePtr was created
349
        if n.1.version < self.versions.len() {
350
            // self.versions contains the number of atoms (.0) and pairs (.1) at
351
            // the specific version
352
            match n.object_type() {
353
                ObjectType::Bytes => {
354
                    assert!(
355
                        (n.index() as usize) < self.versions[n.1.version].0,
356
                        "NodePtr (atom) was invalidated by restore_checkpoint()"
357
                    );
358
                }
359
                ObjectType::Pair => {
360
                    assert!(
361
                        (n.index() as usize) < self.versions[n.1.version].1,
362
                        "NodePtr (pair) was invalidated by restore_checkpoint()"
363
                    );
364
                }
365
                ObjectType::SmallAtom => {}
366
            }
367
        }
368
    }
369

370
    #[inline(always)]
371
    #[cfg(not(feature = "allocator-debug"))]
372
    fn mk_node(&self, t: ObjectType, idx: usize) -> NodePtr {
1,790,161,620✔
373
        NodePtr::new(t, idx)
1,790,161,620✔
374
    }
1,790,161,620✔
375

376
    #[inline(always)]
377
    #[cfg(feature = "allocator-debug")]
378
    fn mk_node(&self, t: ObjectType, idx: usize) -> NodePtr {
379
        NodePtr::new_debug(
380
            t,
381
            idx,
382
            AllocatorReference {
383
                fingerprint: self.fingerprint,
384
                version: self.versions.len(),
385
            },
386
        )
387
    }
388

389
    // create a checkpoint for the current state of the allocator. This can be
390
    // used to go back to an earlier allocator state by passing the Checkpoint
391
    // to restore_checkpoint().
392
    pub fn checkpoint(&self) -> Checkpoint {
66✔
393
        Checkpoint {
66✔
394
            u8s: self.u8_vec.len(),
66✔
395
            pairs: self.pair_vec.len(),
66✔
396
            atoms: self.atom_vec.len(),
66✔
397
            ghost_atoms: self.ghost_atoms,
66✔
398
            ghost_pairs: self.ghost_pairs,
66✔
399
            ghost_heap: self.ghost_heap,
66✔
400
        }
66✔
401
    }
66✔
402

403
    pub fn restore_checkpoint(&mut self, cp: &Checkpoint) {
29✔
404
        // if any of these asserts fire, it means we're trying to restore to
405
        // a state that has already been "long-jumped" passed (via another
406
        // restore to an earlier state). You can only restore backwards in time,
407
        // not forwards.
408
        assert!(self.u8_vec.len() >= cp.u8s);
29✔
409
        assert!(self.pair_vec.len() >= cp.pairs);
29✔
410
        assert!(self.atom_vec.len() >= cp.atoms);
29✔
411
        self.u8_vec.truncate(cp.u8s);
29✔
412
        self.pair_vec.truncate(cp.pairs);
29✔
413
        self.atom_vec.truncate(cp.atoms);
29✔
414
        self.ghost_atoms = cp.ghost_atoms;
29✔
415
        self.ghost_pairs = cp.ghost_pairs;
29✔
416
        self.ghost_heap = cp.ghost_heap;
29✔
417

418
        // This invalidates all NodePtrs with higher index than this, with a
419
        // lower version than self.versions.len()
420
        #[cfg(feature = "allocator-debug")]
421
        self.versions
422
            .push((self.atom_vec.len(), self.pair_vec.len()));
423
    }
29✔
424

425
    pub fn new_atom(&mut self, v: &[u8]) -> Result<NodePtr, EvalErr> {
251,746,557✔
426
        let start = self.u8_vec.len() as u32;
251,746,557✔
427
        if (self.heap_limit - start as usize - self.ghost_heap) < v.len() {
251,746,557✔
428
            return err(self.nil(), "out of memory");
1✔
429
        }
251,746,556✔
430
        let idx = self.atom_vec.len();
251,746,556✔
431
        self.check_atom_limit()?;
251,746,556✔
432
        if let Some(ret) = fits_in_small_atom(v) {
251,746,555✔
433
            self.ghost_atoms += 1;
251,421,752✔
434
            Ok(self.mk_node(ObjectType::SmallAtom, ret as usize))
251,421,752✔
435
        } else {
436
            self.u8_vec.extend_from_slice(v);
324,803✔
437
            let end = self.u8_vec.len() as u32;
324,803✔
438
            self.atom_vec.push(AtomBuf { start, end });
324,803✔
439
            Ok(self.mk_node(ObjectType::Bytes, idx))
324,803✔
440
        }
441
    }
251,746,557✔
442

443
    pub fn new_small_number(&mut self, v: u32) -> Result<NodePtr, EvalErr> {
46,039,593✔
444
        debug_assert!(v <= NODE_PTR_IDX_MASK);
46,039,593✔
445
        self.check_atom_limit()?;
46,039,593✔
446
        self.ghost_atoms += 1;
46,039,592✔
447
        Ok(self.mk_node(ObjectType::SmallAtom, v as usize))
46,039,592✔
448
    }
46,039,593✔
449

450
    pub fn new_number(&mut self, v: Number) -> Result<NodePtr, EvalErr> {
46,331,870✔
451
        use num_traits::ToPrimitive;
452
        if let Some(val) = v.to_u32() {
46,331,870✔
453
            if val <= NODE_PTR_IDX_MASK {
46,038,897✔
454
                return self.new_small_number(val);
46,038,799✔
455
            }
98✔
456
        }
292,973✔
457
        let bytes: Vec<u8> = v.to_signed_bytes_be();
293,071✔
458
        let mut slice = bytes.as_slice();
293,071✔
459

460
        // make number minimal by removing leading zeros
461
        while (!slice.is_empty()) && (slice[0] == 0) {
293,071✔
462
            if slice.len() > 1 && (slice[1] & 0x80 == 0x80) {
32,204✔
463
                break;
32,204✔
464
            }
×
465
            slice = &slice[1..];
×
466
        }
467
        self.new_atom(slice)
293,071✔
468
    }
46,331,870✔
469

470
    pub fn new_g1(&mut self, g1: G1Element) -> Result<NodePtr, EvalErr> {
16,821✔
471
        self.new_atom(&g1.to_bytes())
16,821✔
472
    }
16,821✔
473

474
    pub fn new_g2(&mut self, g2: G2Element) -> Result<NodePtr, EvalErr> {
257✔
475
        self.new_atom(&g2.to_bytes())
257✔
476
    }
257✔
477

478
    pub fn new_pair(&mut self, first: NodePtr, rest: NodePtr) -> Result<NodePtr, EvalErr> {
965,707,303✔
479
        #[cfg(feature = "allocator-debug")]
480
        {
481
            self.validate_node(first);
482
            self.validate_node(rest);
483
        }
484
        let idx = self.pair_vec.len();
965,707,303✔
485
        if idx >= MAX_NUM_PAIRS - self.ghost_pairs {
965,707,303✔
486
            return err(self.nil(), "too many pairs");
16✔
487
        }
965,707,287✔
488
        self.pair_vec.push(IntPair { first, rest });
965,707,287✔
489
        Ok(self.mk_node(ObjectType::Pair, idx))
965,707,287✔
490
    }
965,707,303✔
491

492
    // this code is used when we are simulating pairs with a vec locally
493
    // in the deserialize_br code
494
    // we must maintain parity with the old deserialize_br code so need to track the skipped pairs
495
    pub fn add_ghost_pair(&mut self, amount: usize) -> Result<(), EvalErr> {
2,755✔
496
        if MAX_NUM_PAIRS - self.ghost_pairs - self.pair_vec.len() < amount {
2,755✔
497
            return err(self.nil(), "too many pairs");
2✔
498
        }
2,753✔
499
        self.ghost_pairs += amount;
2,753✔
500
        Ok(())
2,753✔
501
    }
2,755✔
502

503
    // this code is used when we actually create the pairs that were previously skipped ghost pairs
504
    pub fn remove_ghost_pair(&mut self, amount: usize) -> Result<(), EvalErr> {
11✔
505
        // currently let this panic with overflow if we go below 0 to debug if/where it happens
506
        debug_assert!(self.ghost_pairs >= amount);
11✔
507
        self.ghost_pairs -= amount;
11✔
508
        Ok(())
11✔
509
    }
11✔
510

511
    pub fn new_substr(&mut self, node: NodePtr, start: u32, end: u32) -> Result<NodePtr, EvalErr> {
42✔
512
        #[cfg(feature = "allocator-debug")]
513
        self.validate_node(node);
514

515
        self.check_atom_limit()?;
42✔
516

517
        fn bounds_check(node: NodePtr, start: u32, end: u32, len: u32) -> Result<(), EvalErr> {
40✔
518
            if start > len {
40✔
519
                return err(node, "substr start out of bounds");
4✔
520
            }
36✔
521
            if end > len {
36✔
522
                return err(node, "substr end out of bounds");
2✔
523
            }
34✔
524
            if end < start {
34✔
525
                return err(node, "substr invalid bounds");
2✔
526
            }
32✔
527
            Ok(())
32✔
528
        }
40✔
529

530
        match node.object_type() {
41✔
531
            ObjectType::Pair => err(node, "(internal error) substr expected atom, got pair"),
1✔
532
            ObjectType::Bytes => {
533
                let atom = self.atom_vec[node.index() as usize];
28✔
534
                let atom_len = atom.end - atom.start;
28✔
535
                bounds_check(node, start, end, atom_len)?;
28✔
536
                let idx = self.atom_vec.len();
24✔
537
                self.atom_vec.push(AtomBuf {
24✔
538
                    start: atom.start + start,
24✔
539
                    end: atom.start + end,
24✔
540
                });
24✔
541
                Ok(self.mk_node(ObjectType::Bytes, idx))
24✔
542
            }
543
            ObjectType::SmallAtom => {
544
                let val = node.index();
12✔
545
                let len = len_for_value(val) as u32;
12✔
546
                bounds_check(node, start, end, len)?;
12✔
547
                let buf: [u8; 4] = val.to_be_bytes();
8✔
548
                let buf = &buf[4 - len as usize..];
8✔
549
                let substr = &buf[start as usize..end as usize];
8✔
550
                if let Some(new_val) = fits_in_small_atom(substr) {
8✔
551
                    self.ghost_atoms += 1;
6✔
552
                    Ok(self.mk_node(ObjectType::SmallAtom, new_val as usize))
6✔
553
                } else {
554
                    let start = self.u8_vec.len();
2✔
555
                    let end = start + substr.len();
2✔
556
                    self.u8_vec.extend_from_slice(substr);
2✔
557
                    let idx = self.atom_vec.len();
2✔
558
                    self.atom_vec.push(AtomBuf {
2✔
559
                        start: start as u32,
2✔
560
                        end: end as u32,
2✔
561
                    });
2✔
562
                    Ok(self.mk_node(ObjectType::Bytes, idx))
2✔
563
                }
564
            }
565
        }
566
    }
42✔
567

568
    pub fn new_concat(&mut self, new_size: usize, nodes: &[NodePtr]) -> Result<NodePtr, EvalErr> {
90✔
569
        #[cfg(feature = "allocator-debug")]
570
        {
571
            for n in nodes {
572
                self.validate_node(*n);
573
            }
574
        }
575

576
        self.check_atom_limit()?;
90✔
577
        let start = self.u8_vec.len();
89✔
578
        if self.heap_limit - start - self.ghost_heap < new_size {
89✔
579
            return err(self.nil(), "out of memory");
1✔
580
        }
88✔
581

582
        if nodes.is_empty() {
88✔
583
            if 0 != new_size {
6✔
584
                return err(
1✔
585
                    self.nil(),
1✔
586
                    "(internal error) concat passed invalid new_size",
1✔
587
                );
588
            }
5✔
589
            // pretend that we created a new atom and allocated new_size bytes on the heap
590
            self.ghost_atoms += 1;
5✔
591
            return Ok(self.nil());
5✔
592
        }
82✔
593

594
        if nodes.len() == 1 {
82✔
595
            if self.atom_len(nodes[0]) != new_size {
6✔
596
                return err(
1✔
597
                    self.nil(),
1✔
598
                    "(internal error) concat passed invalid new_size",
1✔
599
                );
600
            }
5✔
601
            // pretend that we created a new atom and allocated new_size bytes on the heap
602
            self.ghost_heap += new_size;
5✔
603
            self.ghost_atoms += 1;
5✔
604
            return Ok(nodes[0]);
5✔
605
        }
76✔
606

607
        self.u8_vec.reserve(new_size);
76✔
608

609
        let mut counter: usize = 0;
76✔
610
        for node in nodes {
228✔
611
            match node.object_type() {
158✔
612
                ObjectType::Pair => {
613
                    self.u8_vec.truncate(start);
2✔
614
                    return err(*node, "(internal error) concat expected atom, got pair");
2✔
615
                }
616
                ObjectType::Bytes => {
617
                    let term = self.atom_vec[node.index() as usize];
122✔
618
                    if counter + term.len() > new_size {
122✔
619
                        self.u8_vec.truncate(start);
4✔
620
                        return err(*node, "(internal error) concat passed invalid new_size");
4✔
621
                    }
118✔
622
                    self.u8_vec
118✔
623
                        .extend_from_within(term.start as usize..term.end as usize);
118✔
624
                    counter += term.len();
118✔
625
                }
626
                ObjectType::SmallAtom => {
34✔
627
                    let val = node.index();
34✔
628
                    let len = len_for_value(val) as u32;
34✔
629
                    let buf: [u8; 4] = val.to_be_bytes();
34✔
630
                    let buf = &buf[4 - len as usize..];
34✔
631
                    self.u8_vec.extend_from_slice(buf);
34✔
632
                    counter += len as usize;
34✔
633
                }
34✔
634
            }
635
        }
636
        if counter != new_size {
70✔
637
            self.u8_vec.truncate(start);
4✔
638
            return err(
4✔
639
                self.nil(),
4✔
640
                "(internal error) concat passed invalid new_size",
4✔
641
            );
642
        }
66✔
643
        let end = self.u8_vec.len() as u32;
66✔
644
        let idx = self.atom_vec.len();
66✔
645
        self.atom_vec.push(AtomBuf {
66✔
646
            start: (start as u32),
66✔
647
            end,
66✔
648
        });
66✔
649
        Ok(self.mk_node(ObjectType::Bytes, idx))
66✔
650
    }
90✔
651

652
    pub fn atom_eq(&self, lhs: NodePtr, rhs: NodePtr) -> bool {
34,400,114✔
653
        #[cfg(feature = "allocator-debug")]
654
        {
655
            self.validate_node(lhs);
656
            self.validate_node(rhs);
657
        }
658
        let lhs_type = lhs.object_type();
34,400,114✔
659
        let rhs_type = rhs.object_type();
34,400,114✔
660

661
        match (lhs_type, rhs_type) {
34,400,114✔
662
            (ObjectType::Pair, _) | (_, ObjectType::Pair) => {
663
                panic!("atom_eq() called on pair");
2✔
664
            }
665
            (ObjectType::Bytes, ObjectType::Bytes) => {
666
                let lhs = self.atom_vec[lhs.index() as usize];
3,213✔
667
                let rhs = self.atom_vec[rhs.index() as usize];
3,213✔
668
                self.u8_vec[lhs.start as usize..lhs.end as usize]
3,213✔
669
                    == self.u8_vec[rhs.start as usize..rhs.end as usize]
3,213✔
670
            }
671
            (ObjectType::SmallAtom, ObjectType::SmallAtom) => lhs.index() == rhs.index(),
34,396,849✔
672
            (ObjectType::SmallAtom, ObjectType::Bytes) => {
673
                self.bytes_eq_int(self.atom_vec[rhs.index() as usize], lhs.index())
27✔
674
            }
675
            (ObjectType::Bytes, ObjectType::SmallAtom) => {
676
                self.bytes_eq_int(self.atom_vec[lhs.index() as usize], rhs.index())
23✔
677
            }
678
        }
679
    }
34,400,112✔
680

681
    fn bytes_eq_int(&self, atom: AtomBuf, val: u32) -> bool {
50✔
682
        let len = len_for_value(val) as u32;
50✔
683
        if (atom.end - atom.start) != len {
50✔
684
            return false;
32✔
685
        }
18✔
686
        if val == 0 {
18✔
687
            return true;
5✔
688
        }
13✔
689

690
        if self.u8_vec[atom.start as usize] & 0x80 != 0 {
13✔
691
            // SmallAtom only represents positive values
692
            // if the byte buffer is negative, they can't match
693
            return false;
×
694
        }
13✔
695

696
        // since we know the value of atom is small, we can turn it into a u32 and compare
697
        // against val
698
        let mut atom_val: u32 = 0;
13✔
699
        for i in atom.start..atom.end {
24✔
700
            atom_val <<= 8;
24✔
701
            atom_val |= self.u8_vec[i as usize] as u32;
24✔
702
        }
24✔
703
        val == atom_val
13✔
704
    }
50✔
705

706
    pub fn atom(&self, node: NodePtr) -> Atom {
28,432,234✔
707
        #[cfg(feature = "allocator-debug")]
708
        self.validate_node(node);
709

710
        let index = node.index();
28,432,234✔
711

712
        match node.object_type() {
28,432,234✔
713
            ObjectType::Bytes => {
714
                let atom = self.atom_vec[index as usize];
11,383,102✔
715
                Atom::Borrowed(&self.u8_vec[atom.start as usize..atom.end as usize])
11,383,102✔
716
            }
717
            ObjectType::SmallAtom => {
718
                let len = len_for_value(index);
17,049,132✔
719
                let bytes = index.to_be_bytes();
17,049,132✔
720
                Atom::U32(bytes, len)
17,049,132✔
721
            }
722
            _ => panic!("expected atom, got pair"),
×
723
        }
724
    }
28,432,234✔
725

726
    pub fn atom_len(&self, node: NodePtr) -> usize {
875,372,768✔
727
        #[cfg(feature = "allocator-debug")]
728
        self.validate_node(node);
729

730
        let index = node.index();
875,372,768✔
731

732
        match node.object_type() {
875,372,768✔
733
            ObjectType::Bytes => {
734
                let atom = self.atom_vec[index as usize];
28,788,495✔
735
                (atom.end - atom.start) as usize
28,788,495✔
736
            }
737
            ObjectType::SmallAtom => len_for_value(index),
846,584,272✔
738
            _ => {
739
                panic!("expected atom, got pair");
1✔
740
            }
741
        }
742
    }
875,372,767✔
743

744
    pub fn small_number(&self, node: NodePtr) -> Option<u32> {
1,112,047,748✔
745
        #[cfg(feature = "allocator-debug")]
746
        self.validate_node(node);
747

748
        match node.object_type() {
1,112,047,748✔
749
            ObjectType::SmallAtom => Some(node.index()),
1,089,319,886✔
750
            ObjectType::Bytes => {
751
                let atom = self.atom_vec[node.index() as usize];
22,727,859✔
752
                let buf = &self.u8_vec[atom.start as usize..atom.end as usize];
22,727,859✔
753
                fits_in_small_atom(buf)
22,727,859✔
754
            }
755
            _ => None,
3✔
756
        }
757
    }
1,112,047,748✔
758

759
    pub fn number(&self, node: NodePtr) -> Number {
46,161,734✔
760
        #[cfg(feature = "allocator-debug")]
761
        self.validate_node(node);
762

763
        let index = node.index();
46,161,734✔
764

765
        match node.object_type() {
46,161,734✔
766
            ObjectType::Bytes => {
767
                let atom = self.atom_vec[index as usize];
87,213✔
768
                number_from_u8(&self.u8_vec[atom.start as usize..atom.end as usize])
87,213✔
769
            }
770
            ObjectType::SmallAtom => Number::from(index),
46,074,520✔
771
            _ => {
772
                panic!("number() called on pair");
1✔
773
            }
774
        }
775
    }
46,161,733✔
776

777
    pub fn g1(&self, node: NodePtr) -> Result<G1Element, EvalErr> {
16,745✔
778
        #[cfg(feature = "allocator-debug")]
779
        self.validate_node(node);
780

781
        let idx = match node.object_type() {
16,745✔
782
            ObjectType::Bytes => node.index(),
16,717✔
783
            ObjectType::SmallAtom => {
784
                return err(node, "atom is not G1 size, 48 bytes");
11✔
785
            }
786
            ObjectType::Pair => {
787
                return err(node, "pair found, expected G1 point");
17✔
788
            }
789
        };
790
        let atom = self.atom_vec[idx as usize];
16,717✔
791
        if atom.end - atom.start != 48 {
16,717✔
792
            return err(node, "atom is not G1 size, 48 bytes");
17✔
793
        }
16,700✔
794

795
        let array: &[u8; 48] = &self.u8_vec[atom.start as usize..atom.end as usize]
16,700✔
796
            .try_into()
16,700✔
797
            .expect("atom size is not 48 bytes");
16,700✔
798
        G1Element::from_bytes(array)
16,700✔
799
            .map_err(|_| EvalErr(node, "atom is not a G1 point".to_string()))
16,700✔
800
    }
16,745✔
801

802
    pub fn g2(&self, node: NodePtr) -> Result<G2Element, EvalErr> {
478✔
803
        #[cfg(feature = "allocator-debug")]
804
        self.validate_node(node);
805

806
        let idx = match node.object_type() {
478✔
807
            ObjectType::Bytes => node.index(),
459✔
808
            ObjectType::SmallAtom => {
809
                return err(node, "atom is not G2 size, 96 bytes");
13✔
810
            }
811
            ObjectType::Pair => {
812
                return err(node, "pair found, expected G2 point");
6✔
813
            }
814
        };
815

816
        let atom = self.atom_vec[idx as usize];
459✔
817
        if atom.end - atom.start != 96 {
459✔
818
            return err(node, "atom is not G2 size, 96 bytes");
17✔
819
        }
442✔
820

821
        let array: &[u8; 96] = &self.u8_vec[atom.start as usize..atom.end as usize]
442✔
822
            .try_into()
442✔
823
            .expect("atom size is not 96 bytes");
442✔
824

825
        G2Element::from_bytes(array)
442✔
826
            .map_err(|_| EvalErr(node, "atom is not a G2 point".to_string()))
442✔
827
    }
478✔
828

829
    pub fn node(&self, node: NodePtr) -> NodeVisitor {
381,144,510✔
830
        #[cfg(feature = "allocator-debug")]
831
        self.validate_node(node);
832

833
        let index = node.index();
381,144,510✔
834

835
        match node.object_type() {
381,144,510✔
836
            ObjectType::Bytes => {
837
                let atom = self.atom_vec[index as usize];
417,189✔
838
                let buf = &self.u8_vec[atom.start as usize..atom.end as usize];
417,189✔
839
                NodeVisitor::Buffer(buf)
417,189✔
840
            }
841
            ObjectType::SmallAtom => NodeVisitor::U32(index),
380,718,886✔
842
            ObjectType::Pair => {
843
                let pair = self.pair_vec[index as usize];
8,435✔
844
                NodeVisitor::Pair(pair.first, pair.rest)
8,435✔
845
            }
846
        }
847
    }
381,144,510✔
848

849
    pub fn sexp(&self, node: NodePtr) -> SExp {
2,147,483,647✔
850
        #[cfg(feature = "allocator-debug")]
851
        self.validate_node(node);
852

853
        match node.object_type() {
2,147,483,647✔
854
            ObjectType::Bytes | ObjectType::SmallAtom => SExp::Atom,
1,739,831,026✔
855
            ObjectType::Pair => {
856
                let pair = self.pair_vec[node.index() as usize];
2,147,483,647✔
857
                SExp::Pair(pair.first, pair.rest)
2,147,483,647✔
858
            }
859
        }
860
    }
2,147,483,647✔
861

862
    // this is meant to be used when iterating lists:
863
    // while let Some((i, rest)) = a.next(node) {
864
    //     node = rest;
865
    //     ...
866
    // }
867
    pub fn next(&self, n: NodePtr) -> Option<(NodePtr, NodePtr)> {
1,077,869,629✔
868
        #[cfg(feature = "allocator-debug")]
869
        self.validate_node(n);
870

871
        match self.sexp(n) {
1,077,869,629✔
872
            SExp::Pair(first, rest) => Some((first, rest)),
732,021,019✔
873
            SExp::Atom => None,
345,848,610✔
874
        }
875
    }
1,077,869,629✔
876

877
    pub fn nil(&self) -> NodePtr {
506,650,816✔
878
        self.mk_node(ObjectType::SmallAtom, 0)
506,650,816✔
879
    }
506,650,816✔
880

881
    pub fn one(&self) -> NodePtr {
20,017,272✔
882
        self.mk_node(ObjectType::SmallAtom, 1)
20,017,272✔
883
    }
20,017,272✔
884

885
    #[inline]
886
    fn check_atom_limit(&self) -> Result<(), EvalErr> {
297,786,281✔
887
        if self.atom_vec.len() + self.ghost_atoms == MAX_NUM_ATOMS {
297,786,281✔
888
            err(self.nil(), "too many atoms")
4✔
889
        } else {
890
            Ok(())
297,786,277✔
891
        }
892
    }
297,786,281✔
893

894
    #[cfg(feature = "counters")]
895
    pub fn atom_count(&self) -> usize {
1✔
896
        self.atom_vec.len()
1✔
897
    }
1✔
898

899
    #[cfg(feature = "counters")]
900
    pub fn small_atom_count(&self) -> usize {
1✔
901
        self.ghost_atoms
1✔
902
    }
1✔
903

904
    #[cfg(feature = "counters")]
905
    pub fn pair_count(&self) -> usize {
4✔
906
        self.pair_vec.len() + self.ghost_pairs
4✔
907
    }
4✔
908

909
    #[cfg(feature = "counters")]
910
    pub fn pair_count_no_ghosts(&self) -> usize {
2✔
911
        self.pair_vec.len()
2✔
912
    }
2✔
913

914
    #[cfg(feature = "counters")]
915
    pub fn heap_size(&self) -> usize {
1✔
916
        self.u8_vec.len()
1✔
917
    }
1✔
918
}
919

920
#[cfg(test)]
921
mod tests {
922
    use rstest::rstest;
923

924
    use super::*;
925

926
    #[test]
927
    fn test_atom_eq_1() {
1✔
928
        // these are a bunch of different representations of 1
929
        // make sure they all compare equal
930
        let mut a = Allocator::new();
1✔
931
        let a0 = a.one();
1✔
932
        let a1 = a.new_atom(&[1]).unwrap();
1✔
933
        let a2 = {
1✔
934
            let tmp = a.new_atom(&[0x01, 0xff]).unwrap();
1✔
935
            a.new_substr(tmp, 0, 1).unwrap()
1✔
936
        };
937
        let a3 = a.new_substr(a2, 0, 1).unwrap();
1✔
938
        let a4 = a.new_number(1.into()).unwrap();
1✔
939
        let a5 = a.new_small_number(1).unwrap();
1✔
940

941
        assert!(a.atom_eq(a0, a0));
1✔
942
        assert!(a.atom_eq(a0, a1));
1✔
943
        assert!(a.atom_eq(a0, a2));
1✔
944
        assert!(a.atom_eq(a0, a3));
1✔
945
        assert!(a.atom_eq(a0, a4));
1✔
946
        assert!(a.atom_eq(a0, a5));
1✔
947

948
        assert!(a.atom_eq(a1, a0));
1✔
949
        assert!(a.atom_eq(a1, a1));
1✔
950
        assert!(a.atom_eq(a1, a2));
1✔
951
        assert!(a.atom_eq(a1, a3));
1✔
952
        assert!(a.atom_eq(a1, a4));
1✔
953
        assert!(a.atom_eq(a1, a5));
1✔
954

955
        assert!(a.atom_eq(a2, a0));
1✔
956
        assert!(a.atom_eq(a2, a1));
1✔
957
        assert!(a.atom_eq(a2, a2));
1✔
958
        assert!(a.atom_eq(a2, a3));
1✔
959
        assert!(a.atom_eq(a2, a4));
1✔
960
        assert!(a.atom_eq(a2, a5));
1✔
961

962
        assert!(a.atom_eq(a3, a0));
1✔
963
        assert!(a.atom_eq(a3, a1));
1✔
964
        assert!(a.atom_eq(a3, a2));
1✔
965
        assert!(a.atom_eq(a3, a3));
1✔
966
        assert!(a.atom_eq(a3, a4));
1✔
967
        assert!(a.atom_eq(a3, a5));
1✔
968

969
        assert!(a.atom_eq(a4, a0));
1✔
970
        assert!(a.atom_eq(a4, a1));
1✔
971
        assert!(a.atom_eq(a4, a2));
1✔
972
        assert!(a.atom_eq(a4, a3));
1✔
973
        assert!(a.atom_eq(a4, a4));
1✔
974
        assert!(a.atom_eq(a4, a5));
1✔
975

976
        assert!(a.atom_eq(a5, a0));
1✔
977
        assert!(a.atom_eq(a5, a1));
1✔
978
        assert!(a.atom_eq(a5, a2));
1✔
979
        assert!(a.atom_eq(a5, a3));
1✔
980
        assert!(a.atom_eq(a5, a4));
1✔
981
        assert!(a.atom_eq(a5, a5));
1✔
982
    }
1✔
983

984
    #[test]
985
    fn test_atom_eq_minus_1() {
1✔
986
        // these are a bunch of different representations of -1
987
        // make sure they all compare equal
988
        let mut a = Allocator::new();
1✔
989
        let a0 = a.new_atom(&[0xff]).unwrap();
1✔
990
        let a1 = a.new_number((-1).into()).unwrap();
1✔
991
        let a2 = {
1✔
992
            let tmp = a.new_atom(&[0x01, 0xff]).unwrap();
1✔
993
            a.new_substr(tmp, 1, 2).unwrap()
1✔
994
        };
995
        let a3 = a.new_substr(a0, 0, 1).unwrap();
1✔
996

997
        assert!(a.atom_eq(a0, a0));
1✔
998
        assert!(a.atom_eq(a0, a1));
1✔
999
        assert!(a.atom_eq(a0, a2));
1✔
1000
        assert!(a.atom_eq(a0, a3));
1✔
1001

1002
        assert!(a.atom_eq(a1, a0));
1✔
1003
        assert!(a.atom_eq(a1, a1));
1✔
1004
        assert!(a.atom_eq(a1, a2));
1✔
1005
        assert!(a.atom_eq(a1, a3));
1✔
1006

1007
        assert!(a.atom_eq(a2, a0));
1✔
1008
        assert!(a.atom_eq(a2, a1));
1✔
1009
        assert!(a.atom_eq(a2, a2));
1✔
1010
        assert!(a.atom_eq(a2, a3));
1✔
1011

1012
        assert!(a.atom_eq(a3, a0));
1✔
1013
        assert!(a.atom_eq(a3, a1));
1✔
1014
        assert!(a.atom_eq(a3, a2));
1✔
1015
        assert!(a.atom_eq(a3, a3));
1✔
1016
    }
1✔
1017

1018
    #[test]
1019
    fn test_atom_eq() {
1✔
1020
        let mut a = Allocator::new();
1✔
1021
        let a0 = a.nil();
1✔
1022
        let a1 = a.one();
1✔
1023
        let a2 = a.new_atom(&[1]).unwrap();
1✔
1024
        let a3 = a.new_atom(&[0xfa, 0xc7]).unwrap();
1✔
1025
        let a4 = a.new_small_number(1).unwrap();
1✔
1026
        let a5 = a.new_number((-1337).into()).unwrap();
1✔
1027

1028
        assert!(a.atom_eq(a0, a0));
1✔
1029
        assert!(!a.atom_eq(a0, a1));
1✔
1030
        assert!(!a.atom_eq(a0, a2));
1✔
1031
        assert!(!a.atom_eq(a0, a3));
1✔
1032
        assert!(!a.atom_eq(a0, a4));
1✔
1033
        assert!(!a.atom_eq(a0, a5));
1✔
1034

1035
        assert!(!a.atom_eq(a1, a0));
1✔
1036
        assert!(a.atom_eq(a1, a1));
1✔
1037
        assert!(a.atom_eq(a1, a2));
1✔
1038
        assert!(!a.atom_eq(a1, a3));
1✔
1039
        assert!(a.atom_eq(a1, a4));
1✔
1040
        assert!(!a.atom_eq(a1, a5));
1✔
1041

1042
        assert!(!a.atom_eq(a2, a0));
1✔
1043
        assert!(a.atom_eq(a2, a1));
1✔
1044
        assert!(a.atom_eq(a2, a2));
1✔
1045
        assert!(!a.atom_eq(a2, a3));
1✔
1046
        assert!(a.atom_eq(a2, a4));
1✔
1047
        assert!(!a.atom_eq(a2, a5));
1✔
1048

1049
        assert!(!a.atom_eq(a3, a0));
1✔
1050
        assert!(!a.atom_eq(a3, a1));
1✔
1051
        assert!(!a.atom_eq(a3, a2));
1✔
1052
        assert!(a.atom_eq(a3, a3));
1✔
1053
        assert!(!a.atom_eq(a3, a4));
1✔
1054
        assert!(a.atom_eq(a3, a5));
1✔
1055

1056
        assert!(!a.atom_eq(a4, a0));
1✔
1057
        assert!(a.atom_eq(a4, a1));
1✔
1058
        assert!(a.atom_eq(a4, a2));
1✔
1059
        assert!(!a.atom_eq(a4, a3));
1✔
1060
        assert!(a.atom_eq(a4, a4));
1✔
1061
        assert!(!a.atom_eq(a4, a5));
1✔
1062
    }
1✔
1063

1064
    #[test]
1065
    #[should_panic]
1066
    fn test_atom_eq_pair1() {
1✔
1067
        let mut a = Allocator::new();
1✔
1068
        let a0 = a.nil();
1✔
1069
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1070
        a.atom_eq(pair, a0);
1✔
1071
    }
1✔
1072

1073
    #[test]
1074
    #[should_panic]
1075
    fn test_atom_eq_pair2() {
1✔
1076
        let mut a = Allocator::new();
1✔
1077
        let a0 = a.nil();
1✔
1078
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1079
        a.atom_eq(a0, pair);
1✔
1080
    }
1✔
1081

1082
    #[test]
1083
    #[should_panic]
1084
    fn test_atom_len_pair() {
1✔
1085
        let mut a = Allocator::new();
1✔
1086
        let a0 = a.nil();
1✔
1087
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1088
        a.atom_len(pair);
1✔
1089
    }
1✔
1090

1091
    #[test]
1092
    #[should_panic]
1093
    fn test_number_pair() {
1✔
1094
        let mut a = Allocator::new();
1✔
1095
        let a0 = a.nil();
1✔
1096
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1097
        a.number(pair);
1✔
1098
    }
1✔
1099

1100
    #[cfg(not(feature = "allocator-debug"))]
1101
    #[test]
1102
    #[should_panic]
1103
    fn test_invalid_node_ptr_type() {
1✔
1104
        let node = NodePtr(3 << NODE_PTR_IDX_BITS);
1✔
1105
        // unknown NodePtr type
1106
        let _ = node.object_type();
1✔
1107
    }
1✔
1108

1109
    #[cfg(debug_assertions)]
1110
    #[test]
1111
    #[should_panic]
1112
    fn test_node_ptr_overflow() {
1113
        NodePtr::new(ObjectType::Bytes, NODE_PTR_IDX_MASK as usize + 1);
1114
    }
1115

1116
    #[cfg(debug_assertions)]
1117
    #[test]
1118
    #[should_panic]
1119
    fn test_invalid_small_number() {
1120
        let mut a = Allocator::new();
1121
        a.new_small_number(NODE_PTR_IDX_MASK + 1).unwrap();
1122
    }
1123

1124
    #[rstest]
1125
    #[case(0, 0)]
1126
    #[case(1, 1)]
1127
    #[case(0x7f, 1)]
1128
    #[case(0x80, 2)]
1129
    #[case(0x7fff, 2)]
1130
    #[case(0x7fffff, 3)]
1131
    #[case(0x800000, 4)]
1132
    #[case(0x7fffffff, 4)]
1133
    #[case(0x80000000, 5)]
1134
    #[case(0xffffffff, 5)]
1135
    fn test_len_for_value(#[case] val: u32, #[case] len: usize) {
1136
        assert_eq!(len_for_value(val), len);
1137
    }
1138

1139
    #[test]
1140
    fn test_nil() {
1✔
1141
        let a = Allocator::new();
1✔
1142
        assert_eq!(a.atom(a.nil()).as_ref(), b"");
1✔
1143
        assert_eq!(a.sexp(a.nil()), SExp::Atom);
1✔
1144
        assert_eq!(a.nil(), NodePtr::default());
1✔
1145
        assert_eq!(a.nil(), NodePtr::NIL);
1✔
1146
    }
1✔
1147

1148
    #[test]
1149
    fn test_one() {
1✔
1150
        let a = Allocator::new();
1✔
1151
        assert_eq!(a.atom(a.one()).as_ref(), b"\x01");
1✔
1152
        assert_eq!(a.sexp(a.one()), SExp::Atom);
1✔
1153
    }
1✔
1154

1155
    #[test]
1156
    fn test_allocate_atom() {
1✔
1157
        let mut a = Allocator::new();
1✔
1158
        let atom = a.new_atom(b"foobar").unwrap();
1✔
1159
        assert_eq!(a.atom(atom).as_ref(), b"foobar");
1✔
1160
        assert_eq!(a.sexp(atom), SExp::Atom);
1✔
1161
    }
1✔
1162

1163
    #[test]
1164
    fn test_allocate_pair() {
1✔
1165
        let mut a = Allocator::new();
1✔
1166
        let atom1 = a.new_atom(b"foo").unwrap();
1✔
1167
        let atom2 = a.new_atom(b"bar").unwrap();
1✔
1168
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1169

1170
        assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2));
1✔
1171

1172
        let pair2 = a.new_pair(pair, pair).unwrap();
1✔
1173
        assert_eq!(a.sexp(pair2), SExp::Pair(pair, pair));
1✔
1174
    }
1✔
1175

1176
    #[test]
1177
    fn test_allocate_heap_limit() {
1✔
1178
        let mut a = Allocator::new_limited(6);
1✔
1179
        // we can't allocate 6 bytes
1180
        assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "out of memory");
1✔
1181
        // but 5 is OK
1182
        let _atom = a.new_atom(b"fooba").unwrap();
1✔
1183
    }
1✔
1184

1185
    #[test]
1186
    fn test_allocate_atom_limit() {
1✔
1187
        let mut a = Allocator::new();
1✔
1188

1189
        for _ in 0..MAX_NUM_ATOMS - 2 {
62,499,998✔
1190
            // exhaust the number of atoms allowed to be allocated
62,499,998✔
1191
            let _ = a.new_atom(b"foo").unwrap();
62,499,998✔
1192
        }
62,499,998✔
1193
        assert_eq!(a.new_atom(b"foobar").unwrap_err().1, "too many atoms");
1✔
1194
        assert_eq!(a.u8_vec.len(), 0);
1✔
1195
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1196
    }
1✔
1197

1198
    #[test]
1199
    fn test_allocate_small_number_limit() {
1✔
1200
        let mut a = Allocator::new();
1✔
1201

1202
        for _ in 0..MAX_NUM_ATOMS - 2 {
62,499,998✔
1203
            // exhaust the number of atoms allowed to be allocated
62,499,998✔
1204
            let _ = a.new_atom(b"foo").unwrap();
62,499,998✔
1205
        }
62,499,998✔
1206
        assert_eq!(a.new_small_number(3).unwrap_err().1, "too many atoms");
1✔
1207
        assert_eq!(a.u8_vec.len(), 0);
1✔
1208
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1209
    }
1✔
1210

1211
    #[test]
1212
    fn test_allocate_substr_limit() {
1✔
1213
        let mut a = Allocator::new();
1✔
1214

1215
        for _ in 0..MAX_NUM_ATOMS - 3 {
62,499,997✔
1216
            // exhaust the number of atoms allowed to be allocated
62,499,997✔
1217
            let _ = a.new_atom(b"foo").unwrap();
62,499,997✔
1218
        }
62,499,997✔
1219
        let atom = a.new_atom(b"foo").unwrap();
1✔
1220
        assert_eq!(a.new_substr(atom, 1, 2).unwrap_err().1, "too many atoms");
1✔
1221
        assert_eq!(a.u8_vec.len(), 0);
1✔
1222
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1223
    }
1✔
1224

1225
    #[test]
1226
    fn test_allocate_concat_limit() {
1✔
1227
        let mut a = Allocator::new();
1✔
1228

1229
        for _ in 0..MAX_NUM_ATOMS - 3 {
62,499,997✔
1230
            // exhaust the number of atoms allowed to be allocated
62,499,997✔
1231
            let _ = a.new_atom(b"foo").unwrap();
62,499,997✔
1232
        }
62,499,997✔
1233
        let atom = a.new_atom(b"foo").unwrap();
1✔
1234
        assert_eq!(a.new_concat(3, &[atom]).unwrap_err().1, "too many atoms");
1✔
1235
        assert_eq!(a.u8_vec.len(), 0);
1✔
1236
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1237
    }
1✔
1238

1239
    #[test]
1240
    fn test_allocate_pair_limit() {
1✔
1241
        let mut a = Allocator::new();
1✔
1242
        let atom = a.new_atom(b"foo").unwrap();
1✔
1243
        // one pair is OK
1244
        let _pair1 = a.new_pair(atom, atom).unwrap();
1✔
1245
        for _ in 1..MAX_NUM_PAIRS {
62,500,000✔
1246
            // exhaust the number of pairs allowed to be allocated
62,499,999✔
1247
            let _ = a.new_pair(atom, atom).unwrap();
62,499,999✔
1248
        }
62,499,999✔
1249

1250
        assert_eq!(a.new_pair(atom, atom).unwrap_err().1, "too many pairs");
1✔
1251
        assert_eq!(a.add_ghost_pair(1).unwrap_err().1, "too many pairs");
1✔
1252
    }
1✔
1253

1254
    #[test]
1255
    fn test_ghost_pair_limit() {
1✔
1256
        let mut a = Allocator::new();
1✔
1257
        let atom = a.new_atom(b"foo").unwrap();
1✔
1258
        // one pair is OK
1259
        let _pair1 = a.new_pair(atom, atom).unwrap();
1✔
1260
        a.add_ghost_pair(MAX_NUM_PAIRS - 1).unwrap();
1✔
1261

1262
        assert_eq!(a.new_pair(atom, atom).unwrap_err().1, "too many pairs");
1✔
1263
        assert_eq!(a.add_ghost_pair(1).unwrap_err().1, "too many pairs");
1✔
1264
    }
1✔
1265

1266
    #[test]
1267
    fn test_substr() {
1✔
1268
        let mut a = Allocator::new();
1✔
1269
        let atom = a.new_atom(b"foobar").unwrap();
1✔
1270
        let pair = a.new_pair(atom, atom).unwrap();
1✔
1271

1272
        let sub = a.new_substr(atom, 0, 1).unwrap();
1✔
1273
        assert_eq!(a.atom(sub).as_ref(), b"f");
1✔
1274
        let sub = a.new_substr(atom, 1, 6).unwrap();
1✔
1275
        assert_eq!(a.atom(sub).as_ref(), b"oobar");
1✔
1276
        let sub = a.new_substr(atom, 1, 1).unwrap();
1✔
1277
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1278
        let sub = a.new_substr(atom, 0, 0).unwrap();
1✔
1279
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1280

1281
        assert_eq!(
1✔
1282
            a.new_substr(atom, 1, 0).unwrap_err().1,
1✔
1283
            "substr invalid bounds"
1284
        );
1285
        assert_eq!(
1✔
1286
            a.new_substr(atom, 7, 7).unwrap_err().1,
1✔
1287
            "substr start out of bounds"
1288
        );
1289
        assert_eq!(
1✔
1290
            a.new_substr(atom, 0, 7).unwrap_err().1,
1✔
1291
            "substr end out of bounds"
1292
        );
1293
        assert_eq!(
1✔
1294
            a.new_substr(atom, u32::MAX, 4).unwrap_err().1,
1✔
1295
            "substr start out of bounds"
1296
        );
1297
        assert_eq!(
1✔
1298
            a.new_substr(pair, 0, 0).unwrap_err().1,
1✔
1299
            "(internal error) substr expected atom, got pair"
1300
        );
1301
    }
1✔
1302

1303
    #[test]
1304
    fn test_substr_small_number() {
1✔
1305
        let mut a = Allocator::new();
1✔
1306
        let atom = a.new_atom(b"a\x80").unwrap();
1✔
1307
        assert!(a.small_number(atom).is_some());
1✔
1308

1309
        let sub = a.new_substr(atom, 0, 1).unwrap();
1✔
1310
        assert_eq!(a.atom(sub).as_ref(), b"a");
1✔
1311
        assert!(a.small_number(sub).is_some());
1✔
1312
        let sub = a.new_substr(atom, 1, 2).unwrap();
1✔
1313
        assert_eq!(a.atom(sub).as_ref(), b"\x80");
1✔
1314
        assert!(a.small_number(sub).is_none());
1✔
1315
        let sub = a.new_substr(atom, 1, 1).unwrap();
1✔
1316
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1317
        let sub = a.new_substr(atom, 0, 0).unwrap();
1✔
1318
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1319

1320
        assert_eq!(
1✔
1321
            a.new_substr(atom, 1, 0).unwrap_err().1,
1✔
1322
            "substr invalid bounds"
1323
        );
1324
        assert_eq!(
1✔
1325
            a.new_substr(atom, 3, 3).unwrap_err().1,
1✔
1326
            "substr start out of bounds"
1327
        );
1328
        assert_eq!(
1✔
1329
            a.new_substr(atom, 0, 3).unwrap_err().1,
1✔
1330
            "substr end out of bounds"
1331
        );
1332
        assert_eq!(
1✔
1333
            a.new_substr(atom, u32::MAX, 2).unwrap_err().1,
1✔
1334
            "substr start out of bounds"
1335
        );
1336
    }
1✔
1337

1338
    #[test]
1339
    fn test_concat_launder_small_number() {
1✔
1340
        let mut a = Allocator::new();
1✔
1341
        let atom1 = a.new_small_number(42).expect("new_small_number");
1✔
1342
        assert_eq!(a.small_number(atom1), Some(42));
1✔
1343

1344
        // this "launders" the small number into actually being allocated on the
1345
        // heap
1346
        let atom2 = a
1✔
1347
            .new_concat(1, &[a.nil(), atom1, a.nil()])
1✔
1348
            .expect("new_substr");
1✔
1349

1350
        // even though this atom is allocated on the heap (and not stored as a small
1351
        // int), we can still retrieve it as one. The KLVM interpreter depends on
1352
        // this when matching operators against quote, apply and softfork.
1353
        assert_eq!(a.small_number(atom2), Some(42));
1✔
1354
        assert_eq!(a.atom_len(atom2), 1);
1✔
1355
        assert_eq!(a.atom(atom2).as_ref(), &[42]);
1✔
1356
    }
1✔
1357

1358
    #[test]
1359
    fn test_concat() {
1✔
1360
        let mut a = Allocator::new();
1✔
1361
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1362
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1363
        let atom3 = a.new_atom(b"o").unwrap();
1✔
1364
        let atom4 = a.new_atom(b"b").unwrap();
1✔
1365
        let atom5 = a.new_atom(b"a").unwrap();
1✔
1366
        let atom6 = a.new_atom(b"r").unwrap();
1✔
1367
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1368

1369
        let cat = a
1✔
1370
            .new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6])
1✔
1371
            .unwrap();
1✔
1372
        assert_eq!(a.atom(cat).as_ref(), b"foobar");
1✔
1373

1374
        let cat = a.new_concat(12, &[cat, cat]).unwrap();
1✔
1375
        assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar");
1✔
1376

1377
        assert_eq!(
1✔
1378
            a.new_concat(11, &[cat, cat]).unwrap_err().1,
1✔
1379
            "(internal error) concat passed invalid new_size"
1380
        );
1381
        assert_eq!(
1✔
1382
            a.new_concat(13, &[cat, cat]).unwrap_err().1,
1✔
1383
            "(internal error) concat passed invalid new_size"
1384
        );
1385
        assert_eq!(
1✔
1386
            a.new_concat(12, &[atom3, pair]).unwrap_err().1,
1✔
1387
            "(internal error) concat expected atom, got pair"
1388
        );
1389

1390
        assert_eq!(
1✔
1391
            a.new_concat(4, &[atom1, atom2, atom3]).unwrap_err().1,
1✔
1392
            "(internal error) concat passed invalid new_size"
1393
        );
1394

1395
        assert_eq!(
1✔
1396
            a.new_concat(2, &[atom1, atom2, atom3]).unwrap_err().1,
1✔
1397
            "(internal error) concat passed invalid new_size"
1398
        );
1399

1400
        assert_eq!(
1✔
1401
            a.new_concat(2, &[atom3]).unwrap_err().1,
1✔
1402
            "(internal error) concat passed invalid new_size"
1403
        );
1404
        assert_eq!(
1✔
1405
            a.new_concat(1, &[]).unwrap_err().1,
1✔
1406
            "(internal error) concat passed invalid new_size"
1407
        );
1408

1409
        assert_eq!(a.new_concat(0, &[]).unwrap(), NodePtr::NIL);
1✔
1410
        assert_eq!(a.new_concat(1, &[atom1]).unwrap(), atom1);
1✔
1411
    }
1✔
1412

1413
    #[test]
1414
    fn test_concat_large() {
1✔
1415
        let mut a = Allocator::new();
1✔
1416
        let atom1 = a.new_atom(b"foo").unwrap();
1✔
1417
        let atom2 = a.new_atom(b"bar").unwrap();
1✔
1418
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1419

1420
        let cat = a.new_concat(6, &[atom1, atom2]).unwrap();
1✔
1421
        assert_eq!(a.atom(cat).as_ref(), b"foobar");
1✔
1422

1423
        let cat = a.new_concat(12, &[cat, cat]).unwrap();
1✔
1424
        assert_eq!(a.atom(cat).as_ref(), b"foobarfoobar");
1✔
1425

1426
        assert_eq!(
1✔
1427
            a.new_concat(11, &[cat, cat]).unwrap_err().1,
1✔
1428
            "(internal error) concat passed invalid new_size"
1429
        );
1430
        assert_eq!(
1✔
1431
            a.new_concat(13, &[cat, cat]).unwrap_err().1,
1✔
1432
            "(internal error) concat passed invalid new_size"
1433
        );
1434
        assert_eq!(
1✔
1435
            a.new_concat(12, &[atom1, pair]).unwrap_err().1,
1✔
1436
            "(internal error) concat expected atom, got pair"
1437
        );
1438

1439
        assert_eq!(
1✔
1440
            a.new_concat(4, &[atom1, atom2]).unwrap_err().1,
1✔
1441
            "(internal error) concat passed invalid new_size"
1442
        );
1443

1444
        assert_eq!(
1✔
1445
            a.new_concat(2, &[atom1, atom2]).unwrap_err().1,
1✔
1446
            "(internal error) concat passed invalid new_size"
1447
        );
1448
    }
1✔
1449

1450
    #[test]
1451
    fn test_sexp() {
1✔
1452
        let mut a = Allocator::new();
1✔
1453
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1454
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1455
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1456

1457
        assert_eq!(a.sexp(atom1), SExp::Atom);
1✔
1458
        assert_eq!(a.sexp(atom2), SExp::Atom);
1✔
1459
        assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2));
1✔
1460
    }
1✔
1461

1462
    #[test]
1463
    fn test_concat_limit() {
1✔
1464
        let mut a = Allocator::new_limited(6);
1✔
1465
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1466
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1467
        let atom3 = a.new_atom(b"o").unwrap();
1✔
1468
        let atom4 = a.new_atom(b"b").unwrap();
1✔
1469
        let atom5 = a.new_atom(b"a").unwrap();
1✔
1470
        let atom6 = a.new_atom(b"r").unwrap();
1✔
1471

1472
        // we only have 2 bytes left of allowed heap allocation
1473
        assert_eq!(
1✔
1474
            a.new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6])
1✔
1475
                .unwrap_err()
1✔
1476
                .1,
1477
            "out of memory"
1478
        );
1479
        let cat = a.new_concat(2, &[atom1, atom2]).unwrap();
1✔
1480
        assert_eq!(a.atom(cat).as_ref(), b"fo");
1✔
1481
    }
1✔
1482

1483
    #[rstest]
1484
    #[case(0.into(), &[])]
1485
    #[case(1.into(), &[1])]
1486
    #[case((-1).into(), &[0xff])]
1487
    #[case(0x80.into(), &[0, 0x80])]
1488
    #[case(0xff.into(), &[0, 0xff])]
1489
    #[case(0xffffffff_u64.into(), &[0, 0xff, 0xff, 0xff, 0xff])]
1490
    fn test_new_number(#[case] num: Number, #[case] expected: &[u8]) {
1491
        let mut a = Allocator::new();
1492

1493
        // TEST creating the atom from a Number
1494
        let atom = a.new_number(num.clone()).unwrap();
1495

1496
        // make sure we get back the same number
1497
        assert_eq!(a.number(atom), num);
1498
        assert_eq!(a.atom(atom).as_ref(), expected);
1499
        assert_eq!(number_from_u8(expected), num);
1500

1501
        // TEST creating the atom from a buffer
1502
        let atom = a.new_atom(expected).unwrap();
1503

1504
        // make sure we get back the same number
1505
        assert_eq!(a.number(atom), num);
1506
        assert_eq!(a.atom(atom).as_ref(), expected);
1507
        assert_eq!(number_from_u8(expected), num);
1508
    }
1509

1510
    #[test]
1511
    fn test_checkpoints() {
1✔
1512
        let mut a = Allocator::new();
1✔
1513

1514
        let atom1 = a.new_atom(&[4, 3, 2, 1]).unwrap();
1✔
1515
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1516

1517
        let checkpoint = a.checkpoint();
1✔
1518

1519
        let atom2 = a.new_atom(&[6, 5, 4, 3]).unwrap();
1✔
1520
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1521
        assert!(a.atom(atom2).as_ref() == [6, 5, 4, 3]);
1✔
1522

1523
        // at this point we have two atoms and a checkpoint from before the second
1524
        // atom was created
1525

1526
        // now, restoring the checkpoint state will make atom2 disappear
1527

1528
        a.restore_checkpoint(&checkpoint);
1✔
1529

1530
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1531
        let atom3 = a.new_atom(&[6, 5, 4, 3]).unwrap();
1✔
1532
        assert!(a.atom(atom3).as_ref() == [6, 5, 4, 3]);
1✔
1533

1534
        // since atom2 was removed, atom3 should actually be using that slot
1535
        assert_eq!(atom2, atom3);
1✔
1536
    }
1✔
1537

1538
    fn test_g1(a: &Allocator, n: NodePtr) -> EvalErr {
6✔
1539
        a.g1(n).unwrap_err()
6✔
1540
    }
6✔
1541

1542
    fn test_g2(a: &Allocator, n: NodePtr) -> EvalErr {
6✔
1543
        a.g2(n).unwrap_err()
6✔
1544
    }
6✔
1545

1546
    type TestFun = fn(&Allocator, NodePtr) -> EvalErr;
1547

1548
    #[rstest]
1549
    #[case(test_g1, 0, "atom is not G1 size, 48 bytes")]
1550
    #[case(test_g1, 3, "atom is not G1 size, 48 bytes")]
1551
    #[case(test_g1, 47, "atom is not G1 size, 48 bytes")]
1552
    #[case(test_g1, 49, "atom is not G1 size, 48 bytes")]
1553
    #[case(test_g1, 48, "atom is not a G1 point")]
1554
    #[case(test_g2, 0, "atom is not G2 size, 96 bytes")]
1555
    #[case(test_g2, 3, "atom is not G2 size, 96 bytes")]
1556
    #[case(test_g2, 95, "atom is not G2 size, 96 bytes")]
1557
    #[case(test_g2, 97, "atom is not G2 size, 96 bytes")]
1558
    #[case(test_g2, 96, "atom is not a G2 point")]
1559
    fn test_point_size_error(#[case] fun: TestFun, #[case] size: usize, #[case] expected: &str) {
1560
        let mut a = Allocator::new();
1561
        let mut buf = Vec::<u8>::new();
1562
        buf.resize(size, 0xcc);
1563
        let n = a.new_atom(&buf).unwrap();
1564
        let r = fun(&a, n);
1565
        assert_eq!(r.0, n);
1566
        assert_eq!(r.1, expected.to_string());
1567
    }
1568

1569
    #[rstest]
1570
    #[case(test_g1, "pair found, expected G1 point")]
1571
    #[case(test_g2, "pair found, expected G2 point")]
1572
    fn test_point_atom_pair(#[case] fun: TestFun, #[case] expected: &str) {
1573
        let mut a = Allocator::new();
1574
        let n = a.new_pair(a.nil(), a.one()).unwrap();
1575
        let r = fun(&a, n);
1576
        assert_eq!(r.0, n);
1577
        assert_eq!(r.1, expected.to_string());
1578
    }
1579

1580
    #[rstest]
1581
    #[case(
1582
        "\
1583
97f1d3a73197d7942695638c4fa9ac0f\
1584
c3688c4f9774b905a14e3a3f171bac58\
1585
6c55e83ff97a1aeffb3af00adb22c6bb"
1586
    )]
1587
    #[case(
1588
        "\
1589
a572cbea904d67468808c8eb50a9450c\
1590
9721db309128012543902d0ac358a62a\
1591
e28f75bb8f1c7c42c39a8c5529bf0f4e"
1592
    )]
1593
    fn test_g1_roundtrip(#[case] atom: &str) {
1594
        let mut a = Allocator::new();
1595
        let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap();
1596
        let g1 = a.g1(n).unwrap();
1597
        assert_eq!(hex::encode(g1.to_bytes()), atom);
1598

1599
        let g1_copy = a.new_g1(g1).unwrap();
1600
        let g1_atom = a.atom(g1_copy);
1601
        assert_eq!(hex::encode(g1_atom), atom);
1602

1603
        // try interpreting the point as G1
1604
        assert_eq!(a.g2(n).unwrap_err().1, "atom is not G2 size, 96 bytes");
1605
        assert_eq!(
1606
            a.g2(g1_copy).unwrap_err().1,
1607
            "atom is not G2 size, 96 bytes"
1608
        );
1609

1610
        // try interpreting the point as number
1611
        assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap()));
1612
        assert_eq!(
1613
            a.number(g1_copy),
1614
            number_from_u8(&hex::decode(atom).unwrap())
1615
        );
1616
    }
1617

1618
    #[rstest]
1619
    #[case(
1620
        "\
1621
93e02b6052719f607dacd3a088274f65\
1622
596bd0d09920b61ab5da61bbdc7f5049\
1623
334cf11213945d57e5ac7d055d042b7e\
1624
024aa2b2f08f0a91260805272dc51051\
1625
c6e47ad4fa403b02b4510b647ae3d177\
1626
0bac0326a805bbefd48056c8c121bdb8"
1627
    )]
1628
    #[case(
1629
        "\
1630
aa4edef9c1ed7f729f520e47730a124f\
1631
d70662a904ba1074728114d1031e1572\
1632
c6c886f6b57ec72a6178288c47c33577\
1633
1638533957d540a9d2370f17cc7ed586\
1634
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1635
81f14b0bf3611b78c952aacab827a053"
1636
    )]
1637
    fn test_g2_roundtrip(#[case] atom: &str) {
1638
        let mut a = Allocator::new();
1639
        let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap();
1640
        let g2 = a.g2(n).unwrap();
1641
        assert_eq!(hex::encode(g2.to_bytes()), atom);
1642

1643
        let g2_copy = a.new_g2(g2).unwrap();
1644
        let g2_atom = a.atom(g2_copy);
1645
        assert_eq!(hex::encode(g2_atom), atom);
1646

1647
        // try interpreting the point as G1
1648
        assert_eq!(a.g1(n).unwrap_err().1, "atom is not G1 size, 48 bytes");
1649
        assert_eq!(
1650
            a.g1(g2_copy).unwrap_err().1,
1651
            "atom is not G1 size, 48 bytes"
1652
        );
1653

1654
        // try interpreting the point as number
1655
        assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap()));
1656
        assert_eq!(
1657
            a.number(g2_copy),
1658
            number_from_u8(&hex::decode(atom).unwrap())
1659
        );
1660
    }
1661

1662
    type MakeFun = fn(&mut Allocator, &[u8]) -> NodePtr;
1663

1664
    fn make_buf(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
16✔
1665
        a.new_atom(bytes).unwrap()
16✔
1666
    }
16✔
1667

1668
    fn make_number(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
16✔
1669
        let v = number_from_u8(bytes);
16✔
1670
        a.new_number(v).unwrap()
16✔
1671
    }
16✔
1672

1673
    fn make_g1(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
4✔
1674
        let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1675
        a.new_g1(v).unwrap()
4✔
1676
    }
4✔
1677

1678
    fn make_g2(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
4✔
1679
        let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1680
        a.new_g2(v).unwrap()
4✔
1681
    }
4✔
1682

1683
    fn make_g1_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
12✔
1684
        assert!(<[u8; 48]>::try_from(bytes).is_err());
12✔
1685
        a.new_atom(bytes).unwrap()
12✔
1686
    }
12✔
1687

1688
    fn make_g2_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
12✔
1689
        assert!(<[u8; 96]>::try_from(bytes).is_err());
12✔
1690
        a.new_atom(bytes).unwrap()
12✔
1691
    }
12✔
1692

1693
    type CheckFun = fn(&Allocator, NodePtr, &[u8]);
1694

1695
    fn check_buf(a: &Allocator, n: NodePtr, bytes: &[u8]) {
16✔
1696
        let buf = a.atom(n);
16✔
1697
        assert_eq!(buf.as_ref(), bytes);
16✔
1698
    }
16✔
1699

1700
    fn check_number(a: &Allocator, n: NodePtr, bytes: &[u8]) {
16✔
1701
        let num = a.number(n);
16✔
1702
        let v = number_from_u8(bytes);
16✔
1703
        assert_eq!(num, v);
16✔
1704
    }
16✔
1705

1706
    fn check_g1(a: &Allocator, n: NodePtr, bytes: &[u8]) {
4✔
1707
        let num = a.g1(n).unwrap();
4✔
1708
        let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1709
        assert_eq!(num, v);
4✔
1710
    }
4✔
1711

1712
    fn check_g2(a: &Allocator, n: NodePtr, bytes: &[u8]) {
4✔
1713
        let num = a.g2(n).unwrap();
4✔
1714
        let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1715
        assert_eq!(num, v);
4✔
1716
    }
4✔
1717

1718
    fn check_g1_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) {
12✔
1719
        assert_eq!(a.g1(n).unwrap_err().0, n);
12✔
1720
        assert!(<[u8; 48]>::try_from(bytes).is_err());
12✔
1721
    }
12✔
1722

1723
    fn check_g2_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) {
12✔
1724
        assert_eq!(a.g2(n).unwrap_err().0, n);
12✔
1725
        assert!(<[u8; 96]>::try_from(bytes).is_err());
12✔
1726
    }
12✔
1727

1728
    const EMPTY: &str = "";
1729

1730
    const SMALL_BUF: &str = "133742";
1731

1732
    const VALID_G1: &str = "\
1733
a572cbea904d67468808c8eb50a9450c\
1734
9721db309128012543902d0ac358a62a\
1735
e28f75bb8f1c7c42c39a8c5529bf0f4e";
1736

1737
    const VALID_G2: &str = "\
1738
aa4edef9c1ed7f729f520e47730a124f\
1739
d70662a904ba1074728114d1031e1572\
1740
c6c886f6b57ec72a6178288c47c33577\
1741
1638533957d540a9d2370f17cc7ed586\
1742
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1743
81f14b0bf3611b78c952aacab827a053";
1744

1745
    /*
1746
      We want to exercise round-tripping avery kind of value via every other kind
1747
      of value (as far as possible). e.g. Every value can round-trip through a byte buffer
1748
      or a number, but G1 cannot round-trip via G2.
1749

1750
      +-----------+--------+--------+------+------+
1751
      | from / to | buffer | number | G1   | G2   |
1752
      +-----------+--------+--------+------+------+
1753
      | buffer    | o      | o      | -    | -    |
1754
      | number    | o      | o      | -    | -    |
1755
      | G1        | o      | o      | o    | -    |
1756
      | G2        | o      | o      | -    | o    |
1757
      +-----------+--------+--------+------+------+
1758

1759
    */
1760

1761
    #[rstest]
1762
    // round trip empty buffer
1763
    #[case(EMPTY, make_buf, check_buf)]
1764
    #[case(EMPTY, make_buf, check_number)]
1765
    #[case(EMPTY, make_buf, check_g1_fail)]
1766
    #[case(EMPTY, make_buf, check_g2_fail)]
1767
    #[case(EMPTY, make_number, check_buf)]
1768
    #[case(EMPTY, make_number, check_number)]
1769
    #[case(EMPTY, make_number, check_g1_fail)]
1770
    #[case(EMPTY, make_number, check_g2_fail)]
1771
    #[case(EMPTY, make_g1_fail, check_buf)]
1772
    #[case(EMPTY, make_g1_fail, check_number)]
1773
    #[case(EMPTY, make_g1_fail, check_g1_fail)]
1774
    #[case(EMPTY, make_g1_fail, check_g2_fail)]
1775
    #[case(EMPTY, make_g2_fail, check_buf)]
1776
    #[case(EMPTY, make_g2_fail, check_number)]
1777
    #[case(EMPTY, make_g2_fail, check_g1_fail)]
1778
    #[case(EMPTY, make_g2_fail, check_g2_fail)]
1779
    // round trip small buffer
1780
    #[case(SMALL_BUF, make_buf, check_buf)]
1781
    #[case(SMALL_BUF, make_buf, check_number)]
1782
    #[case(SMALL_BUF, make_buf, check_g1_fail)]
1783
    #[case(SMALL_BUF, make_buf, check_g2_fail)]
1784
    #[case(SMALL_BUF, make_number, check_buf)]
1785
    #[case(SMALL_BUF, make_number, check_number)]
1786
    #[case(SMALL_BUF, make_number, check_g1_fail)]
1787
    #[case(SMALL_BUF, make_number, check_g2_fail)]
1788
    #[case(SMALL_BUF, make_g1_fail, check_buf)]
1789
    #[case(SMALL_BUF, make_g1_fail, check_number)]
1790
    #[case(SMALL_BUF, make_g1_fail, check_g1_fail)]
1791
    #[case(SMALL_BUF, make_g1_fail, check_g2_fail)]
1792
    #[case(SMALL_BUF, make_g2_fail, check_buf)]
1793
    #[case(SMALL_BUF, make_g2_fail, check_number)]
1794
    #[case(SMALL_BUF, make_g2_fail, check_g1_fail)]
1795
    #[case(SMALL_BUF, make_g2_fail, check_g2_fail)]
1796
    // round trip G1 point
1797
    #[case(VALID_G1, make_buf, check_buf)]
1798
    #[case(VALID_G1, make_buf, check_number)]
1799
    #[case(VALID_G1, make_buf, check_g1)]
1800
    #[case(VALID_G1, make_buf, check_g2_fail)]
1801
    #[case(VALID_G1, make_number, check_buf)]
1802
    #[case(VALID_G1, make_number, check_number)]
1803
    #[case(VALID_G1, make_number, check_g1)]
1804
    #[case(VALID_G1, make_number, check_g2_fail)]
1805
    #[case(VALID_G1, make_g1, check_buf)]
1806
    #[case(VALID_G1, make_g1, check_number)]
1807
    #[case(VALID_G1, make_g1, check_g1)]
1808
    #[case(VALID_G1, make_g1, check_g2_fail)]
1809
    #[case(VALID_G1, make_g2_fail, check_buf)]
1810
    #[case(VALID_G1, make_g2_fail, check_number)]
1811
    #[case(VALID_G1, make_g2_fail, check_g1)]
1812
    #[case(VALID_G1, make_g2_fail, check_g2_fail)]
1813
    // round trip G2 point
1814
    #[case(VALID_G2, make_buf, check_buf)]
1815
    #[case(VALID_G2, make_buf, check_number)]
1816
    #[case(VALID_G2, make_buf, check_g1_fail)]
1817
    #[case(VALID_G2, make_buf, check_g2)]
1818
    #[case(VALID_G2, make_number, check_buf)]
1819
    #[case(VALID_G2, make_number, check_number)]
1820
    #[case(VALID_G2, make_number, check_g1_fail)]
1821
    #[case(VALID_G2, make_number, check_g2)]
1822
    #[case(VALID_G2, make_g1_fail, check_buf)]
1823
    #[case(VALID_G2, make_g1_fail, check_number)]
1824
    #[case(VALID_G2, make_g1_fail, check_g1_fail)]
1825
    #[case(VALID_G2, make_g1_fail, check_g2)]
1826
    #[case(VALID_G2, make_g2, check_buf)]
1827
    #[case(VALID_G2, make_g2, check_number)]
1828
    #[case(VALID_G2, make_g2, check_g1_fail)]
1829
    #[case(VALID_G2, make_g2, check_g2)]
1830
    fn test_roundtrip(#[case] test_value: &str, #[case] make: MakeFun, #[case] check: CheckFun) {
1831
        let value = hex::decode(test_value).unwrap();
1832
        let mut a = Allocator::new();
1833
        let node = make(&mut a, &value);
1834
        check(&a, node, &value);
1835
    }
1836

1837
    #[rstest]
1838
    #[case(&[], 0)]
1839
    #[case(&[1], 1)]
1840
    #[case(&[1,2], 2)]
1841
    #[case(&[1,2,3,4,5,6,7,8,9], 9)]
1842
    #[case(&[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], 18)]
1843
    fn test_atom_len(#[case] buf: &[u8], #[case] expected: usize) {
1844
        let mut a = Allocator::new();
1845
        let atom = a.new_atom(buf).unwrap();
1846
        assert_eq!(a.atom_len(atom), expected);
1847
    }
1848

1849
    #[rstest]
1850
    #[case(0.into(), 0)]
1851
    #[case(42.into(), 1)]
1852
    #[case(127.into(), 1)]
1853
    #[case(1337.into(), 2)]
1854
    #[case(0x7fffff.into(), 3)]
1855
    #[case(0xffffff.into(), 4)]
1856
    #[case((-1).into(), 1)]
1857
    #[case((-128).into(), 1)]
1858
    fn test_atom_len_number(#[case] value: Number, #[case] expected: usize) {
1859
        let mut a = Allocator::new();
1860
        let atom = a.new_number(value).unwrap();
1861
        assert_eq!(a.atom_len(atom), expected);
1862
    }
1863

1864
    #[rstest]
1865
    #[case(
1866
        "\
1867
97f1d3a73197d7942695638c4fa9ac0f\
1868
c3688c4f9774b905a14e3a3f171bac58\
1869
6c55e83ff97a1aeffb3af00adb22c6bb",
1870
        48
1871
    )]
1872
    #[case(
1873
        "\
1874
a572cbea904d67468808c8eb50a9450c\
1875
9721db309128012543902d0ac358a62a\
1876
e28f75bb8f1c7c42c39a8c5529bf0f4e",
1877
        48
1878
    )]
1879
    fn test_atom_len_g1(#[case] buffer_hex: &str, #[case] expected: usize) {
1880
        let mut a = Allocator::new();
1881
        let buffer = &hex::decode(buffer_hex).unwrap();
1882
        let g1 = G1Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G1 point");
1883
        let atom = a.new_g1(g1).unwrap();
1884
        assert_eq!(a.atom_len(atom), expected);
1885
    }
1886

1887
    #[rstest]
1888
    #[case(
1889
        "\
1890
93e02b6052719f607dacd3a088274f65\
1891
596bd0d09920b61ab5da61bbdc7f5049\
1892
334cf11213945d57e5ac7d055d042b7e\
1893
024aa2b2f08f0a91260805272dc51051\
1894
c6e47ad4fa403b02b4510b647ae3d177\
1895
0bac0326a805bbefd48056c8c121bdb8",
1896
        96
1897
    )]
1898
    #[case(
1899
        "\
1900
aa4edef9c1ed7f729f520e47730a124f\
1901
d70662a904ba1074728114d1031e1572\
1902
c6c886f6b57ec72a6178288c47c33577\
1903
1638533957d540a9d2370f17cc7ed586\
1904
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1905
81f14b0bf3611b78c952aacab827a053",
1906
        96
1907
    )]
1908
    fn test_atom_len_g2(#[case] buffer_hex: &str, #[case] expected: usize) {
1909
        let mut a = Allocator::new();
1910

1911
        let buffer = &hex::decode(buffer_hex).unwrap();
1912
        let g2 = G2Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G2 point");
1913
        let atom = a.new_g2(g2).unwrap();
1914
        assert_eq!(a.atom_len(atom), expected);
1915
    }
1916

1917
    #[rstest]
1918
    #[case(0.into())]
1919
    #[case(1.into())]
1920
    #[case(0x7f.into())]
1921
    #[case(0x80.into())]
1922
    #[case(0xff.into())]
1923
    #[case(0x100.into())]
1924
    #[case(0x7fff.into())]
1925
    #[case(0x8000.into())]
1926
    #[case(0xffff.into())]
1927
    #[case(0x10000.into())]
1928
    #[case(0x7ffff.into())]
1929
    #[case(0x80000.into())]
1930
    #[case(0xfffff.into())]
1931
    #[case(0x100000.into())]
1932
    #[case(0x7ffffff.into())]
1933
    #[case(0x8000000.into())]
1934
    #[case(0xfffffff.into())]
1935
    #[case(0x10000000.into())]
1936
    #[case(0x7ffffffff_u64.into())]
1937
    #[case(0x8000000000_u64.into())]
1938
    #[case(0xffffffffff_u64.into())]
1939
    #[case(0x10000000000_u64.into())]
1940
    #[case((-1).into())]
1941
    #[case((-0x7f).into())]
1942
    #[case((-0x80).into())]
1943
    #[case((-0xff).into())]
1944
    #[case((-0x100).into())]
1945
    #[case((-0x7fff).into())]
1946
    #[case((-0x8000).into())]
1947
    #[case((-0xffff).into())]
1948
    #[case((-0x10000).into())]
1949
    #[case((-0x7ffff).into())]
1950
    #[case((-0x80000).into())]
1951
    #[case((-0xfffff).into())]
1952
    #[case((-0x100000).into())]
1953
    #[case((-0x7ffffff_i64).into())]
1954
    #[case((-0x8000000_i64).into())]
1955
    #[case((-0xfffffff_i64).into())]
1956
    #[case((-0x10000000_i64).into())]
1957
    #[case((-0x7ffffffff_i64).into())]
1958
    #[case((-0x8000000000_i64).into())]
1959
    #[case((-0xffffffffff_i64).into())]
1960
    #[case((-0x10000000000_i64).into())]
1961
    fn test_number_roundtrip(#[case] value: Number) {
1962
        let mut a = Allocator::new();
1963
        let atom = a.new_number(value.clone()).expect("new_number()");
1964
        assert_eq!(a.number(atom), value);
1965
    }
1966

1967
    #[rstest]
1968
    #[case(0)]
1969
    #[case(1)]
1970
    #[case(0x7f)]
1971
    #[case(0x80)]
1972
    #[case(0xff)]
1973
    #[case(0x100)]
1974
    #[case(0x7fff)]
1975
    #[case(0x8000)]
1976
    #[case(0xffff)]
1977
    #[case(0x10000)]
1978
    #[case(0x7ffff)]
1979
    #[case(0x80000)]
1980
    #[case(0xfffff)]
1981
    #[case(0x100000)]
1982
    #[case(0x7fffff)]
1983
    #[case(0x800000)]
1984
    #[case(0xffffff)]
1985
    #[case(0x1000000)]
1986
    #[case(0x3ffffff)]
1987
    fn test_small_number_roundtrip(#[case] value: u32) {
1988
        let mut a = Allocator::new();
1989
        let atom = a.new_small_number(value).expect("new_small_number()");
1990
        assert_eq!(a.small_number(atom).expect("small_number()"), value);
1991
    }
1992

1993
    #[rstest]
1994
    #[case(0.into(), true)]
1995
    #[case(1.into(), true)]
1996
    #[case(0x3ffffff.into(), true)]
1997
    #[case(0x4000000.into(), false)]
1998
    #[case(0x7f.into(), true)]
1999
    #[case(0x80.into(), true)]
2000
    #[case(0xff.into(), true)]
2001
    #[case(0x100.into(), true)]
2002
    #[case(0x7fff.into(), true)]
2003
    #[case(0x8000.into(), true)]
2004
    #[case(0xffff.into(), true)]
2005
    #[case(0x10000.into(), true)]
2006
    #[case(0x7ffff.into(), true)]
2007
    #[case(0x80000.into(), true)]
2008
    #[case(0xfffff.into(), true)]
2009
    #[case(0x100000.into(), true)]
2010
    #[case(0x7ffffff.into(), false)]
2011
    #[case(0x8000000.into(), false)]
2012
    #[case(0xfffffff.into(), false)]
2013
    #[case(0x10000000.into(), false)]
2014
    #[case(0x7ffffffff_u64.into(), false)]
2015
    #[case(0x8000000000_u64.into(), false )]
2016
    #[case(0xffffffffff_u64.into(), false)]
2017
    #[case(0x10000000000_u64.into(), false)]
2018
    #[case((-1).into(), false)]
2019
    #[case((-0x7f).into(), false)]
2020
    #[case((-0x80).into(), false)]
2021
    #[case((-0x10000000000_i64).into(), false)]
2022
    fn test_auto_small_number(#[case] value: Number, #[case] expect_small: bool) {
2023
        let mut a = Allocator::new();
2024
        let atom = a.new_number(value.clone()).expect("new_number()");
2025
        assert_eq!(a.small_number(atom).is_some(), expect_small);
2026
        if let Some(v) = a.small_number(atom) {
2027
            use num_traits::ToPrimitive;
2028
            assert_eq!(v, value.to_u32().unwrap());
2029
        }
2030
        assert_eq!(a.number(atom), value);
2031
    }
2032

2033
    #[rstest]
2034
    // redundant leading zeros are not canoncial
2035
    #[case(&[0x00], false)]
2036
    #[case(&[0x00, 0x7f], false)]
2037
    // negative numbers cannot be small ints
2038
    #[case(&[0x80], false)]
2039
    #[case(&[0xff], false)]
2040
    #[case(&[0xff, 0xff], false)]
2041
    #[case(&[0x80, 0xff, 0xff], false)]
2042
    // small positive intergers can be small
2043
    #[case(&[0x01], true)]
2044
    #[case(&[0x00, 0xff], true)]
2045
    #[case(&[0x7f, 0xff], true)]
2046
    #[case(&[0x7f, 0xff, 0xff], true)]
2047
    #[case(&[0x00, 0xff, 0xff, 0xff], true)]
2048
    #[case(&[0x02, 0x00, 0x00, 0x00], true)]
2049
    #[case(&[0x03, 0xff, 0xff, 0xff], true)]
2050
    // too big
2051
    #[case(&[0x04, 0x00, 0x00, 0x00], false)]
2052
    fn test_auto_small_number_from_buf(#[case] buf: &[u8], #[case] expect_small: bool) {
2053
        let mut a = Allocator::new();
2054
        let atom = a.new_atom(buf).expect("new_atom()");
2055
        assert_eq!(a.small_number(atom).is_some(), expect_small);
2056
        if let Some(v) = a.small_number(atom) {
2057
            use num_traits::ToPrimitive;
2058
            assert_eq!(v, a.number(atom).to_u32().expect("to_u32()"));
2059
        }
2060
        assert_eq!(buf, a.atom(atom).as_ref());
2061
    }
2062

2063
    #[rstest]
2064
    // redundant leading zeros are not canoncial
2065
    #[case(&[0x00], None)]
2066
    #[case(&[0x00, 0x7f], None)]
2067
    // negative numbers cannot be small ints
2068
    #[case(&[0x80], None)]
2069
    #[case(&[0xff], None)]
2070
    // redundant leading 0xff are still negative
2071
    #[case(&[0xff, 0xff], None)]
2072
    #[case(&[0x80, 0xff, 0xff], None)]
2073
    // to big
2074
    #[case(&[0x04, 0x00, 0x00, 0x00], None)]
2075
    #[case(&[0x05, 0x00, 0x00, 0x00], None)]
2076
    #[case(&[0x04, 0x00, 0x00, 0x00, 0x00], None)]
2077
    // small positive intergers can be small
2078
    #[case(&[0x01], Some(0x01))]
2079
    #[case(&[0x00, 0x80], Some(0x80))]
2080
    #[case(&[0x00, 0xff], Some(0xff))]
2081
    #[case(&[0x7f, 0xff], Some(0x7fff))]
2082
    #[case(&[0x00, 0x80, 0x00], Some(0x8000))]
2083
    #[case(&[0x00, 0xff, 0xff], Some(0xffff))]
2084
    #[case(&[0x7f, 0xff, 0xff], Some(0x7fffff))]
2085
    #[case(&[0x00, 0x80, 0x00, 0x00], Some(0x800000))]
2086
    #[case(&[0x00, 0xff, 0xff, 0xff], Some(0xffffff))]
2087
    #[case(&[0x02, 0x00, 0x00, 0x00], Some(0x2000000))]
2088
    #[case(&[0x03, 0x00, 0x00, 0x00], Some(0x3000000))]
2089
    #[case(&[0x03, 0xff, 0xff, 0xff], Some(0x3ffffff))]
2090
    fn test_fits_in_small_atom(#[case] buf: &[u8], #[case] expected: Option<u32>) {
2091
        assert_eq!(fits_in_small_atom(buf), expected);
2092
    }
2093

2094
    #[rstest]
2095
    // 0 is encoded as an empty string
2096
    #[case(&[0], "0", &[])]
2097
    #[case(&[1], "1", &[1])]
2098
    // leading zeroes are redundant
2099
    #[case(&[0,0,0,1], "1", &[1])]
2100
    #[case(&[0,0,0x80], "128", &[0, 0x80])]
2101
    // A leading zero is necessary to encode a positive number with the
2102
    // penultimate byte's most significant bit set
2103
    #[case(&[0,0xff], "255", &[0, 0xff])]
2104
    #[case(&[0x7f,0xff], "32767", &[0x7f, 0xff])]
2105
    // the first byte is redundant, it's still -1
2106
    #[case(&[0xff,0xff], "-1", &[0xff])]
2107
    #[case(&[0xff], "-1", &[0xff])]
2108
    #[case(&[0,0,0x80,0], "32768", &[0,0x80,0])]
2109
    #[case(&[0,0,0x40,0], "16384", &[0x40,0])]
2110
    fn test_number_to_atom(#[case] bytes: &[u8], #[case] text: &str, #[case] buf: &[u8]) {
2111
        let mut a = Allocator::new();
2112

2113
        // 0 is encoded as an empty string
2114
        let num = number_from_u8(bytes);
2115
        assert_eq!(format!("{}", num), text);
2116
        let ptr = a.new_number(num).unwrap();
2117
        assert_eq!(a.atom(ptr).as_ref(), buf);
2118
    }
2119
}
2120

2121
#[cfg(feature = "allocator-debug")]
2122
#[cfg(test)]
2123
mod debug_tests {
2124
    use super::*;
2125
    use chik_bls::PublicKey;
2126
    use chik_bls::Signature;
2127
    use rstest::rstest;
2128

2129
    fn new_node(a: &mut Allocator, case: u8) -> (NodePtr, usize) {
2130
        match case {
2131
            0 => (a.nil(), 0),
2132
            1 => (a.one(), 1),
2133
            2 => (a.new_atom(b"foobar").expect("new_atom"), 6),
2134
            3 => (a.new_pair(NodePtr::NIL, NodePtr::NIL).expect("new_pair"), 0),
2135
            4 => (a.new_concat(2, &[a.one(), a.one()]).expect("new_concat"), 2),
2136
            5 => (a.new_substr(a.one(), 0, 1).expect("new_substr"), 1),
2137
            6 => (a.new_small_number(1337).expect("new_small_number"), 2),
2138
            7 => (a.new_number(u32::MAX.into()).expect("new_number"), 5),
2139
            8 => (a.new_g1(PublicKey::default()).expect("new_g1"), 48),
2140
            9 => (a.new_g2(Signature::default()).expect("new_g2"), 32),
2141
            _ => {
2142
                panic!("unexpected case");
2143
            }
2144
        }
2145
    }
2146

2147
    fn access_node(a: &mut Allocator, n: NodePtr, len: usize, case: u8) {
2148
        match case {
2149
            0 => {
2150
                let _ = a.new_pair(n, a.nil());
2151
            }
2152
            1 => {
2153
                let _ = a.new_pair(a.nil(), n);
2154
            }
2155
            2 => {
2156
                let _ = a.new_substr(n, 0, (len / 2) as u32);
2157
            }
2158
            3 => {
2159
                let _ = a.new_concat(len, &[n]);
2160
            }
2161
            4 => {
2162
                let _ = a.new_concat(len + 1, &[a.one(), n]);
2163
            }
2164
            5 => {
2165
                a.atom_eq(a.one(), n);
2166
            }
2167
            6 => {
2168
                a.atom_eq(n, a.one());
2169
            }
2170
            7 => {
2171
                a.atom(n);
2172
            }
2173
            8 => {
2174
                a.atom_len(n);
2175
            }
2176
            9 => {
2177
                a.small_number(n);
2178
            }
2179
            10 => {
2180
                a.number(n);
2181
            }
2182
            11 => {
2183
                let _ = a.g1(n);
2184
            }
2185
            12 => {
2186
                let _ = a.g2(n);
2187
            }
2188
            13 => {
2189
                a.node(n);
2190
            }
2191
            14 => {
2192
                a.sexp(n);
2193
            }
2194
            15 => {
2195
                a.next(n);
2196
            }
2197
            _ => {
2198
                panic!("unexpected case");
2199
            }
2200
        }
2201
    }
2202

2203
    #[rstest]
2204
    #[should_panic(expected = "using a NodePtr on the wrong Allocator")]
2205
    fn mixing_allocators(
2206
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)] create_case: u8,
2207
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] access_case: u8,
2208
    ) {
2209
        let mut a1 = Allocator::new();
2210
        let mut a2 = Allocator::new();
2211

2212
        let (node, len) = new_node(&mut a1, create_case);
2213
        access_node(&mut a2, node, len, access_case);
2214
    }
2215

2216
    #[rstest]
2217
    #[should_panic(expected = "was invalidated by restore_checkpoint()")]
2218
    fn invalidating_node(
2219
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)] create_case: u8,
2220
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] access_case: u8,
2221
    ) {
2222
        let mut a1 = Allocator::new();
2223
        let checkpoint = a1.checkpoint();
2224

2225
        let (node, len) = new_node(&mut a1, create_case);
2226

2227
        // this will invalidate "node"
2228
        a1.restore_checkpoint(&checkpoint);
2229
        access_node(&mut a1, node, len, access_case);
2230

2231
        if matches!(a1.node(node), NodeVisitor::U32(_)) {
2232
            // small atoms aren't allocated on the heap, so we don't know
2233
            // whether they should have been invalidated or not. They never are
2234
            // in practice.
2235
            panic!("simulated NodePtr was invalidated by restore_checkpoint()");
2236
        }
2237
    }
2238
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc