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

Chik-Network / klvm_rs / 16770817670

06 Aug 2025 07:46AM UTC coverage: 93.355% (-0.3%) from 93.675%
16770817670

push

github

Chik-Network
update 0.16.0

526 of 581 new or added lines in 26 files covered. (90.53%)

1 existing line in 1 file now uncovered.

6294 of 6742 relevant lines covered (93.36%)

29671867.37 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

449
    pub fn new_number(&mut self, v: Number) -> Result<NodePtr> {
46,331,306✔
450
        use num_traits::ToPrimitive;
451
        if let Some(val) = v.to_u32() {
46,331,306✔
452
            if val <= NODE_PTR_IDX_MASK {
46,038,408✔
453
                return self.new_small_number(val);
46,038,314✔
454
            }
94✔
455
        }
292,898✔
456
        let bytes: Vec<u8> = v.to_signed_bytes_be();
292,992✔
457
        let mut slice = bytes.as_slice();
292,992✔
458

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

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

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

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

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

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

510
    pub fn add_ghost_atom(&mut self, amount: usize) -> Result<()> {
564✔
511
        if MAX_NUM_ATOMS - self.ghost_atoms - self.atom_vec.len() < amount {
564✔
NEW
512
            return Err(EvalErr::TooManyAtoms);
×
513
        }
564✔
514
        self.ghost_atoms += amount;
564✔
515
        Ok(())
564✔
516
    }
564✔
517
    pub fn new_substr(&mut self, node: NodePtr, start: u32, end: u32) -> Result<NodePtr> {
43✔
518
        #[cfg(feature = "allocator-debug")]
519
        self.validate_node(node);
520

521
        self.check_atom_limit()?;
43✔
522

523
        fn bounds_check(node: NodePtr, start: u32, end: u32, len: u32) -> Result<()> {
41✔
524
            if start > len {
41✔
525
                Err(EvalErr::InvalidAllocArg(
5✔
526
                    node,
5✔
527
                    format!("substr start out of bounds: {start} > {len}"),
5✔
528
                ))?;
5✔
529
            }
36✔
530
            if end > len {
36✔
531
                Err(EvalErr::InvalidAllocArg(
2✔
532
                    node,
2✔
533
                    format!("substr end out of bounds: {end} > {len}"),
2✔
534
                ))?;
2✔
535
            }
34✔
536
            if end < start {
34✔
537
                Err(EvalErr::InvalidAllocArg(
2✔
538
                    node,
2✔
539
                    format!("substr invalid bounds: {end} < {start}"),
2✔
540
                ))?;
2✔
541
            }
32✔
542
            Ok(())
32✔
543
        }
41✔
544

545
        match node.object_type() {
42✔
546
            ObjectType::Pair => Err(EvalErr::InternalError(
1✔
547
                node,
1✔
548
                "substr expected atom, got pair".to_string(),
1✔
549
            ))?,
1✔
550
            ObjectType::Bytes => {
551
                let atom = self.atom_vec[node.index() as usize];
28✔
552
                let atom_len = atom.end - atom.start;
28✔
553
                bounds_check(node, start, end, atom_len)?;
28✔
554
                let idx = self.atom_vec.len();
24✔
555
                self.atom_vec.push(AtomBuf {
24✔
556
                    start: atom.start + start,
24✔
557
                    end: atom.start + end,
24✔
558
                });
24✔
559
                Ok(self.mk_node(ObjectType::Bytes, idx))
24✔
560
            }
561
            ObjectType::SmallAtom => {
562
                let val = node.index();
13✔
563
                let len = len_for_value(val) as u32;
13✔
564
                bounds_check(node, start, end, len)?;
13✔
565
                let buf: [u8; 4] = val.to_be_bytes();
8✔
566
                let buf = &buf[4 - len as usize..];
8✔
567
                let substr = &buf[start as usize..end as usize];
8✔
568
                if let Some(new_val) = fits_in_small_atom(substr) {
8✔
569
                    self.ghost_atoms += 1;
6✔
570
                    Ok(self.mk_node(ObjectType::SmallAtom, new_val as usize))
6✔
571
                } else {
572
                    let start = self.u8_vec.len();
2✔
573
                    let end = start + substr.len();
2✔
574
                    self.u8_vec.extend_from_slice(substr);
2✔
575
                    let idx = self.atom_vec.len();
2✔
576
                    self.atom_vec.push(AtomBuf {
2✔
577
                        start: start as u32,
2✔
578
                        end: end as u32,
2✔
579
                    });
2✔
580
                    Ok(self.mk_node(ObjectType::Bytes, idx))
2✔
581
                }
582
            }
583
        }
584
    }
43✔
585

586
    pub fn new_concat(&mut self, new_size: usize, nodes: &[NodePtr]) -> Result<NodePtr> {
90✔
587
        #[cfg(feature = "allocator-debug")]
588
        {
589
            for n in nodes {
590
                self.validate_node(*n);
591
            }
592
        }
593

594
        self.check_atom_limit()?;
90✔
595
        let start = self.u8_vec.len();
89✔
596
        if self.heap_limit - start - self.ghost_heap < new_size {
89✔
597
            return Err(EvalErr::OutOfMemory);
1✔
598
        }
88✔
599

600
        if nodes.is_empty() {
88✔
601
            if 0 != new_size {
6✔
602
                return Err(EvalErr::InternalError(
1✔
603
                    self.nil(),
1✔
604
                    "concat passed invalid new_size".to_string(),
1✔
605
                ))?;
1✔
606
            }
5✔
607
            // pretend that we created a new atom and allocated new_size bytes on the heap
608
            self.ghost_atoms += 1;
5✔
609
            return Ok(self.nil());
5✔
610
        }
82✔
611

612
        if nodes.len() == 1 {
82✔
613
            if self.atom_len(nodes[0]) != new_size {
6✔
614
                return Err(EvalErr::InternalError(
1✔
615
                    self.nil(),
1✔
616
                    "concat passed invalid new_size".to_string(),
1✔
617
                ))?;
1✔
618
            }
5✔
619
            // pretend that we created a new atom and allocated new_size bytes on the heap
620
            self.ghost_heap += new_size;
5✔
621
            self.ghost_atoms += 1;
5✔
622
            return Ok(nodes[0]);
5✔
623
        }
76✔
624

625
        self.u8_vec.reserve(new_size);
76✔
626

627
        let mut counter: usize = 0;
76✔
628
        for node in nodes {
228✔
629
            match node.object_type() {
158✔
630
                ObjectType::Pair => {
631
                    self.u8_vec.truncate(start);
2✔
632
                    return Err(EvalErr::InternalError(
2✔
633
                        *node,
2✔
634
                        "concat expected atom, got pair".to_string(),
2✔
635
                    ))?;
2✔
636
                }
637
                ObjectType::Bytes => {
638
                    let term = self.atom_vec[node.index() as usize];
122✔
639
                    if counter + term.len() > new_size {
122✔
640
                        self.u8_vec.truncate(start);
4✔
641
                        return Err(EvalErr::InternalError(
4✔
642
                            *node,
4✔
643
                            "concat passed invalid new_size".to_string(),
4✔
644
                        ))?;
4✔
645
                    }
118✔
646
                    self.u8_vec
118✔
647
                        .extend_from_within(term.start as usize..term.end as usize);
118✔
648
                    counter += term.len();
118✔
649
                }
650
                ObjectType::SmallAtom => {
34✔
651
                    let val = node.index();
34✔
652
                    let len = len_for_value(val) as u32;
34✔
653
                    let buf: [u8; 4] = val.to_be_bytes();
34✔
654
                    let buf = &buf[4 - len as usize..];
34✔
655
                    self.u8_vec.extend_from_slice(buf);
34✔
656
                    counter += len as usize;
34✔
657
                }
34✔
658
            }
659
        }
660
        if counter != new_size {
70✔
661
            self.u8_vec.truncate(start);
4✔
662
            return Err(EvalErr::InternalError(
4✔
663
                self.nil(),
4✔
664
                "concat passed invalid new_size".to_string(),
4✔
665
            ))?;
4✔
666
        }
66✔
667
        let end = self.u8_vec.len() as u32;
66✔
668
        let idx = self.atom_vec.len();
66✔
669
        self.atom_vec.push(AtomBuf {
66✔
670
            start: start as u32,
66✔
671
            end,
66✔
672
        });
66✔
673
        Ok(self.mk_node(ObjectType::Bytes, idx))
66✔
674
    }
90✔
675

676
    pub fn atom_eq(&self, lhs: NodePtr, rhs: NodePtr) -> bool {
34,400,114✔
677
        #[cfg(feature = "allocator-debug")]
678
        {
679
            self.validate_node(lhs);
680
            self.validate_node(rhs);
681
        }
682
        let lhs_type = lhs.object_type();
34,400,114✔
683
        let rhs_type = rhs.object_type();
34,400,114✔
684

685
        match (lhs_type, rhs_type) {
34,400,114✔
686
            (ObjectType::Pair, _) | (_, ObjectType::Pair) => {
687
                panic!("atom_eq() called on pair");
2✔
688
            }
689
            (ObjectType::Bytes, ObjectType::Bytes) => {
690
                let lhs = self.atom_vec[lhs.index() as usize];
3,213✔
691
                let rhs = self.atom_vec[rhs.index() as usize];
3,213✔
692
                self.u8_vec[lhs.start as usize..lhs.end as usize]
3,213✔
693
                    == self.u8_vec[rhs.start as usize..rhs.end as usize]
3,213✔
694
            }
695
            (ObjectType::SmallAtom, ObjectType::SmallAtom) => lhs.index() == rhs.index(),
34,396,849✔
696
            (ObjectType::SmallAtom, ObjectType::Bytes) => {
697
                self.bytes_eq_int(self.atom_vec[rhs.index() as usize], lhs.index())
27✔
698
            }
699
            (ObjectType::Bytes, ObjectType::SmallAtom) => {
700
                self.bytes_eq_int(self.atom_vec[lhs.index() as usize], rhs.index())
23✔
701
            }
702
        }
703
    }
34,400,112✔
704

705
    fn bytes_eq_int(&self, atom: AtomBuf, val: u32) -> bool {
50✔
706
        let len = len_for_value(val) as u32;
50✔
707
        if (atom.end - atom.start) != len {
50✔
708
            return false;
32✔
709
        }
18✔
710
        if val == 0 {
18✔
711
            return true;
5✔
712
        }
13✔
713

714
        if self.u8_vec[atom.start as usize] & 0x80 != 0 {
13✔
715
            // SmallAtom only represents positive values
716
            // if the byte buffer is negative, they can't match
717
            return false;
×
718
        }
13✔
719

720
        // since we know the value of atom is small, we can turn it into a u32 and compare
721
        // against val
722
        let mut atom_val: u32 = 0;
13✔
723
        for i in atom.start..atom.end {
24✔
724
            atom_val <<= 8;
24✔
725
            atom_val |= self.u8_vec[i as usize] as u32;
24✔
726
        }
24✔
727
        val == atom_val
13✔
728
    }
50✔
729

730
    pub fn atom(&self, node: NodePtr) -> Atom {
28,432,234✔
731
        #[cfg(feature = "allocator-debug")]
732
        self.validate_node(node);
733

734
        let index = node.index();
28,432,234✔
735

736
        match node.object_type() {
28,432,234✔
737
            ObjectType::Bytes => {
738
                let atom = self.atom_vec[index as usize];
11,383,102✔
739
                Atom::Borrowed(&self.u8_vec[atom.start as usize..atom.end as usize])
11,383,102✔
740
            }
741
            ObjectType::SmallAtom => {
742
                let len = len_for_value(index);
17,049,132✔
743
                let bytes = index.to_be_bytes();
17,049,132✔
744
                Atom::U32(bytes, len)
17,049,132✔
745
            }
746
            _ => panic!("expected atom, got pair"),
×
747
        }
748
    }
28,432,234✔
749

750
    pub fn atom_len(&self, node: NodePtr) -> usize {
875,372,768✔
751
        #[cfg(feature = "allocator-debug")]
752
        self.validate_node(node);
753

754
        let index = node.index();
875,372,768✔
755

756
        match node.object_type() {
875,372,768✔
757
            ObjectType::Bytes => {
758
                let atom = self.atom_vec[index as usize];
28,788,495✔
759
                (atom.end - atom.start) as usize
28,788,495✔
760
            }
761
            ObjectType::SmallAtom => len_for_value(index),
846,584,272✔
762
            _ => {
763
                panic!("expected atom, got pair");
1✔
764
            }
765
        }
766
    }
875,372,767✔
767

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

772
        match node.object_type() {
1,112,047,748✔
773
            ObjectType::SmallAtom => Some(node.index()),
1,089,319,886✔
774
            ObjectType::Bytes => {
775
                let atom = self.atom_vec[node.index() as usize];
22,727,859✔
776
                let buf = &self.u8_vec[atom.start as usize..atom.end as usize];
22,727,859✔
777
                fits_in_small_atom(buf)
22,727,859✔
778
            }
779
            _ => None,
3✔
780
        }
781
    }
1,112,047,748✔
782

783
    pub fn number(&self, node: NodePtr) -> Number {
46,161,734✔
784
        #[cfg(feature = "allocator-debug")]
785
        self.validate_node(node);
786

787
        let index = node.index();
46,161,734✔
788

789
        match node.object_type() {
46,161,734✔
790
            ObjectType::Bytes => {
791
                let atom = self.atom_vec[index as usize];
87,213✔
792
                number_from_u8(&self.u8_vec[atom.start as usize..atom.end as usize])
87,213✔
793
            }
794
            ObjectType::SmallAtom => Number::from(index),
46,074,520✔
795
            _ => {
796
                panic!("number() called on pair");
1✔
797
            }
798
        }
799
    }
46,161,733✔
800

801
    pub fn g1(&self, node: NodePtr) -> Result<G1Element> {
16,745✔
802
        #[cfg(feature = "allocator-debug")]
803
        self.validate_node(node);
804

805
        let idx = match node.object_type() {
16,745✔
806
            ObjectType::Bytes => node.index(),
16,717✔
807
            ObjectType::SmallAtom => {
808
                return Err(EvalErr::InvalidAllocArg(
11✔
809
                    node,
11✔
810
                    "atom is not G1 size, 48 bytes".to_string(),
11✔
811
                ))?;
11✔
812
            }
813
            ObjectType::Pair => {
814
                return Err(EvalErr::InvalidAllocArg(
17✔
815
                    node,
17✔
816
                    "pair found, expected G1 point".to_string(),
17✔
817
                ))?;
17✔
818
            }
819
        };
820
        let atom = self.atom_vec[idx as usize];
16,717✔
821
        if atom.end - atom.start != 48 {
16,717✔
822
            return Err(EvalErr::InvalidAllocArg(
17✔
823
                node,
17✔
824
                "atom is not G1 size, 48 bytes".to_string(),
17✔
825
            ))?;
17✔
826
        }
16,700✔
827

828
        let array: &[u8; 48] = &self.u8_vec[atom.start as usize..atom.end as usize]
16,700✔
829
            .try_into()
16,700✔
830
            .map_err(|_| {
16,700✔
NEW
831
                EvalErr::InvalidAllocArg(node, "atom is not G1 size, 48 bytes".to_string())
×
NEW
832
            })?;
×
833
        G1Element::from_bytes(array)
16,700✔
834
            .map_err(|_| EvalErr::InvalidAllocArg(node, "atom is not a G1 point".to_string()))
16,700✔
835
    }
16,745✔
836

837
    pub fn g2(&self, node: NodePtr) -> Result<G2Element> {
478✔
838
        #[cfg(feature = "allocator-debug")]
839
        self.validate_node(node);
840

841
        let idx = match node.object_type() {
478✔
842
            ObjectType::Bytes => node.index(),
459✔
843
            ObjectType::SmallAtom => {
844
                return Err(EvalErr::InvalidAllocArg(
13✔
845
                    node,
13✔
846
                    "atom is not G2 size, 96 bytes".to_string(),
13✔
847
                ))?;
13✔
848
            }
849
            ObjectType::Pair => {
850
                return Err(EvalErr::InvalidAllocArg(
6✔
851
                    node,
6✔
852
                    "pair found, expected G2 point".to_string(),
6✔
853
                ))?;
6✔
854
            }
855
        };
856

857
        let atom = self.atom_vec[idx as usize];
459✔
858

859
        let array: &[u8; 96] = &self.u8_vec[atom.start as usize..atom.end as usize]
459✔
860
            .try_into()
459✔
861
            .map_err(|_| {
459✔
862
                EvalErr::InvalidAllocArg(node, "atom is not G2 size, 96 bytes".to_string())
17✔
863
            })?;
17✔
864

865
        G2Element::from_bytes(array)
442✔
866
            .map_err(|_| EvalErr::InvalidAllocArg(node, "atom is not a G2 point".to_string()))
442✔
867
    }
478✔
868

869
    pub fn node(&self, node: NodePtr) -> NodeVisitor {
381,144,510✔
870
        #[cfg(feature = "allocator-debug")]
871
        self.validate_node(node);
872

873
        let index = node.index();
381,144,510✔
874

875
        match node.object_type() {
381,144,510✔
876
            ObjectType::Bytes => {
877
                let atom = self.atom_vec[index as usize];
417,189✔
878
                let buf = &self.u8_vec[atom.start as usize..atom.end as usize];
417,189✔
879
                NodeVisitor::Buffer(buf)
417,189✔
880
            }
881
            ObjectType::SmallAtom => NodeVisitor::U32(index),
380,718,886✔
882
            ObjectType::Pair => {
883
                let pair = self.pair_vec[index as usize];
8,435✔
884
                NodeVisitor::Pair(pair.first, pair.rest)
8,435✔
885
            }
886
        }
887
    }
381,144,510✔
888

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

893
        match node.object_type() {
2,147,483,647✔
894
            ObjectType::Bytes | ObjectType::SmallAtom => SExp::Atom,
1,739,831,026✔
895
            ObjectType::Pair => {
896
                let pair = self.pair_vec[node.index() as usize];
2,147,483,647✔
897
                SExp::Pair(pair.first, pair.rest)
2,147,483,647✔
898
            }
899
        }
900
    }
2,147,483,647✔
901

902
    // this is meant to be used when iterating lists:
903
    // while let Some((i, rest)) = a.next(node) {
904
    //     node = rest;
905
    //     ...
906
    // }
907
    pub fn next(&self, n: NodePtr) -> Option<(NodePtr, NodePtr)> {
1,077,869,629✔
908
        #[cfg(feature = "allocator-debug")]
909
        self.validate_node(n);
910

911
        match self.sexp(n) {
1,077,869,629✔
912
            SExp::Pair(first, rest) => Some((first, rest)),
732,021,019✔
913
            SExp::Atom => None,
345,848,610✔
914
        }
915
    }
1,077,869,629✔
916

917
    pub fn nil(&self) -> NodePtr {
506,650,785✔
918
        self.mk_node(ObjectType::SmallAtom, 0)
506,650,785✔
919
    }
506,650,785✔
920

921
    pub fn one(&self) -> NodePtr {
20,017,272✔
922
        self.mk_node(ObjectType::SmallAtom, 1)
20,017,272✔
923
    }
20,017,272✔
924

925
    #[inline]
926
    fn check_atom_limit(&self) -> Result<()> {
297,785,718✔
927
        if self.atom_vec.len() + self.ghost_atoms == MAX_NUM_ATOMS {
297,785,718✔
928
            Err(EvalErr::TooManyAtoms)
4✔
929
        } else {
930
            Ok(())
297,785,714✔
931
        }
932
    }
297,785,718✔
933

934
    #[cfg(feature = "counters")]
935
    pub fn atom_count(&self) -> usize {
1✔
936
        self.atom_vec.len()
1✔
937
    }
1✔
938

939
    #[cfg(feature = "counters")]
940
    pub fn small_atom_count(&self) -> usize {
1✔
941
        self.ghost_atoms
1✔
942
    }
1✔
943

944
    #[cfg(feature = "counters")]
945
    pub fn pair_count(&self) -> usize {
4✔
946
        self.pair_vec.len() + self.ghost_pairs
4✔
947
    }
4✔
948

949
    #[cfg(feature = "counters")]
950
    pub fn pair_count_no_ghosts(&self) -> usize {
2✔
951
        self.pair_vec.len()
2✔
952
    }
2✔
953

954
    #[cfg(feature = "counters")]
955
    pub fn heap_size(&self) -> usize {
1✔
956
        self.u8_vec.len()
1✔
957
    }
1✔
958
}
959

960
#[cfg(test)]
961
mod tests {
962
    use super::*;
963
    use rstest::rstest;
964

965
    #[test]
966
    fn test_atom_eq_1() {
1✔
967
        // these are a bunch of different representations of 1
968
        // make sure they all compare equal
969
        let mut a = Allocator::new();
1✔
970
        let a0 = a.one();
1✔
971
        let a1 = a.new_atom(&[1]).unwrap();
1✔
972
        let a2 = {
1✔
973
            let tmp = a.new_atom(&[0x01, 0xff]).unwrap();
1✔
974
            a.new_substr(tmp, 0, 1).unwrap()
1✔
975
        };
976
        let a3 = a.new_substr(a2, 0, 1).unwrap();
1✔
977
        let a4 = a.new_number(1.into()).unwrap();
1✔
978
        let a5 = a.new_small_number(1).unwrap();
1✔
979

980
        assert!(a.atom_eq(a0, a0));
1✔
981
        assert!(a.atom_eq(a0, a1));
1✔
982
        assert!(a.atom_eq(a0, a2));
1✔
983
        assert!(a.atom_eq(a0, a3));
1✔
984
        assert!(a.atom_eq(a0, a4));
1✔
985
        assert!(a.atom_eq(a0, a5));
1✔
986

987
        assert!(a.atom_eq(a1, a0));
1✔
988
        assert!(a.atom_eq(a1, a1));
1✔
989
        assert!(a.atom_eq(a1, a2));
1✔
990
        assert!(a.atom_eq(a1, a3));
1✔
991
        assert!(a.atom_eq(a1, a4));
1✔
992
        assert!(a.atom_eq(a1, a5));
1✔
993

994
        assert!(a.atom_eq(a2, a0));
1✔
995
        assert!(a.atom_eq(a2, a1));
1✔
996
        assert!(a.atom_eq(a2, a2));
1✔
997
        assert!(a.atom_eq(a2, a3));
1✔
998
        assert!(a.atom_eq(a2, a4));
1✔
999
        assert!(a.atom_eq(a2, a5));
1✔
1000

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

1008
        assert!(a.atom_eq(a4, a0));
1✔
1009
        assert!(a.atom_eq(a4, a1));
1✔
1010
        assert!(a.atom_eq(a4, a2));
1✔
1011
        assert!(a.atom_eq(a4, a3));
1✔
1012
        assert!(a.atom_eq(a4, a4));
1✔
1013
        assert!(a.atom_eq(a4, a5));
1✔
1014

1015
        assert!(a.atom_eq(a5, a0));
1✔
1016
        assert!(a.atom_eq(a5, a1));
1✔
1017
        assert!(a.atom_eq(a5, a2));
1✔
1018
        assert!(a.atom_eq(a5, a3));
1✔
1019
        assert!(a.atom_eq(a5, a4));
1✔
1020
        assert!(a.atom_eq(a5, a5));
1✔
1021
    }
1✔
1022

1023
    #[test]
1024
    fn test_atom_eq_minus_1() {
1✔
1025
        // these are a bunch of different representations of -1
1026
        // make sure they all compare equal
1027
        let mut a = Allocator::new();
1✔
1028
        let a0 = a.new_atom(&[0xff]).unwrap();
1✔
1029
        let a1 = a.new_number((-1).into()).unwrap();
1✔
1030
        let a2 = {
1✔
1031
            let tmp = a.new_atom(&[0x01, 0xff]).unwrap();
1✔
1032
            a.new_substr(tmp, 1, 2).unwrap()
1✔
1033
        };
1034
        let a3 = a.new_substr(a0, 0, 1).unwrap();
1✔
1035

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

1041
        assert!(a.atom_eq(a1, a0));
1✔
1042
        assert!(a.atom_eq(a1, a1));
1✔
1043
        assert!(a.atom_eq(a1, a2));
1✔
1044
        assert!(a.atom_eq(a1, a3));
1✔
1045

1046
        assert!(a.atom_eq(a2, a0));
1✔
1047
        assert!(a.atom_eq(a2, a1));
1✔
1048
        assert!(a.atom_eq(a2, a2));
1✔
1049
        assert!(a.atom_eq(a2, a3));
1✔
1050

1051
        assert!(a.atom_eq(a3, a0));
1✔
1052
        assert!(a.atom_eq(a3, a1));
1✔
1053
        assert!(a.atom_eq(a3, a2));
1✔
1054
        assert!(a.atom_eq(a3, a3));
1✔
1055
    }
1✔
1056

1057
    #[test]
1058
    fn test_atom_eq() {
1✔
1059
        let mut a = Allocator::new();
1✔
1060
        let a0 = a.nil();
1✔
1061
        let a1 = a.one();
1✔
1062
        let a2 = a.new_atom(&[1]).unwrap();
1✔
1063
        let a3 = a.new_atom(&[0xfa, 0xc7]).unwrap();
1✔
1064
        let a4 = a.new_small_number(1).unwrap();
1✔
1065
        let a5 = a.new_number((-1337).into()).unwrap();
1✔
1066

1067
        assert!(a.atom_eq(a0, a0));
1✔
1068
        assert!(!a.atom_eq(a0, a1));
1✔
1069
        assert!(!a.atom_eq(a0, a2));
1✔
1070
        assert!(!a.atom_eq(a0, a3));
1✔
1071
        assert!(!a.atom_eq(a0, a4));
1✔
1072
        assert!(!a.atom_eq(a0, a5));
1✔
1073

1074
        assert!(!a.atom_eq(a1, a0));
1✔
1075
        assert!(a.atom_eq(a1, a1));
1✔
1076
        assert!(a.atom_eq(a1, a2));
1✔
1077
        assert!(!a.atom_eq(a1, a3));
1✔
1078
        assert!(a.atom_eq(a1, a4));
1✔
1079
        assert!(!a.atom_eq(a1, a5));
1✔
1080

1081
        assert!(!a.atom_eq(a2, a0));
1✔
1082
        assert!(a.atom_eq(a2, a1));
1✔
1083
        assert!(a.atom_eq(a2, a2));
1✔
1084
        assert!(!a.atom_eq(a2, a3));
1✔
1085
        assert!(a.atom_eq(a2, a4));
1✔
1086
        assert!(!a.atom_eq(a2, a5));
1✔
1087

1088
        assert!(!a.atom_eq(a3, a0));
1✔
1089
        assert!(!a.atom_eq(a3, a1));
1✔
1090
        assert!(!a.atom_eq(a3, a2));
1✔
1091
        assert!(a.atom_eq(a3, a3));
1✔
1092
        assert!(!a.atom_eq(a3, a4));
1✔
1093
        assert!(a.atom_eq(a3, a5));
1✔
1094

1095
        assert!(!a.atom_eq(a4, a0));
1✔
1096
        assert!(a.atom_eq(a4, a1));
1✔
1097
        assert!(a.atom_eq(a4, a2));
1✔
1098
        assert!(!a.atom_eq(a4, a3));
1✔
1099
        assert!(a.atom_eq(a4, a4));
1✔
1100
        assert!(!a.atom_eq(a4, a5));
1✔
1101
    }
1✔
1102

1103
    #[test]
1104
    #[should_panic]
1105
    fn test_atom_eq_pair1() {
1✔
1106
        let mut a = Allocator::new();
1✔
1107
        let a0 = a.nil();
1✔
1108
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1109
        a.atom_eq(pair, a0);
1✔
1110
    }
1✔
1111

1112
    #[test]
1113
    #[should_panic]
1114
    fn test_atom_eq_pair2() {
1✔
1115
        let mut a = Allocator::new();
1✔
1116
        let a0 = a.nil();
1✔
1117
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1118
        a.atom_eq(a0, pair);
1✔
1119
    }
1✔
1120

1121
    #[test]
1122
    #[should_panic]
1123
    fn test_atom_len_pair() {
1✔
1124
        let mut a = Allocator::new();
1✔
1125
        let a0 = a.nil();
1✔
1126
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1127
        a.atom_len(pair);
1✔
1128
    }
1✔
1129

1130
    #[test]
1131
    #[should_panic]
1132
    fn test_number_pair() {
1✔
1133
        let mut a = Allocator::new();
1✔
1134
        let a0 = a.nil();
1✔
1135
        let pair = a.new_pair(a0, a0).unwrap();
1✔
1136
        a.number(pair);
1✔
1137
    }
1✔
1138

1139
    #[cfg(not(feature = "allocator-debug"))]
1140
    #[test]
1141
    #[should_panic]
1142
    fn test_invalid_node_ptr_type() {
1✔
1143
        let node = NodePtr(3 << NODE_PTR_IDX_BITS);
1✔
1144
        // unknown NodePtr type
1145
        let _ = node.object_type();
1✔
1146
    }
1✔
1147

1148
    #[cfg(debug_assertions)]
1149
    #[test]
1150
    #[should_panic]
1151
    fn test_node_ptr_overflow() {
1152
        NodePtr::new(ObjectType::Bytes, NODE_PTR_IDX_MASK as usize + 1);
1153
    }
1154

1155
    #[cfg(debug_assertions)]
1156
    #[test]
1157
    #[should_panic]
1158
    fn test_invalid_small_number() {
1159
        let mut a = Allocator::new();
1160
        a.new_small_number(NODE_PTR_IDX_MASK + 1).unwrap();
1161
    }
1162

1163
    #[rstest]
1164
    #[case(0, 0)]
1165
    #[case(1, 1)]
1166
    #[case(0x7f, 1)]
1167
    #[case(0x80, 2)]
1168
    #[case(0x7fff, 2)]
1169
    #[case(0x7fffff, 3)]
1170
    #[case(0x800000, 4)]
1171
    #[case(0x7fffffff, 4)]
1172
    #[case(0x80000000, 5)]
1173
    #[case(0xffffffff, 5)]
1174
    fn test_len_for_value(#[case] val: u32, #[case] len: usize) {
1175
        assert_eq!(len_for_value(val), len);
1176
    }
1177

1178
    #[test]
1179
    fn test_nil() {
1✔
1180
        let a = Allocator::new();
1✔
1181
        assert_eq!(a.atom(a.nil()).as_ref(), b"");
1✔
1182
        assert_eq!(a.sexp(a.nil()), SExp::Atom);
1✔
1183
        assert_eq!(a.nil(), NodePtr::default());
1✔
1184
        assert_eq!(a.nil(), NodePtr::NIL);
1✔
1185
    }
1✔
1186

1187
    #[test]
1188
    fn test_one() {
1✔
1189
        let a = Allocator::new();
1✔
1190
        assert_eq!(a.atom(a.one()).as_ref(), b"\x01");
1✔
1191
        assert_eq!(a.sexp(a.one()), SExp::Atom);
1✔
1192
    }
1✔
1193

1194
    #[test]
1195
    fn test_allocate_atom() {
1✔
1196
        let mut a = Allocator::new();
1✔
1197
        let atom = a.new_atom(b"foobar").unwrap();
1✔
1198
        assert_eq!(a.atom(atom).as_ref(), b"foobar");
1✔
1199
        assert_eq!(a.sexp(atom), SExp::Atom);
1✔
1200
    }
1✔
1201

1202
    #[test]
1203
    fn test_allocate_pair() {
1✔
1204
        let mut a = Allocator::new();
1✔
1205
        let atom1 = a.new_atom(b"foo").unwrap();
1✔
1206
        let atom2 = a.new_atom(b"bar").unwrap();
1✔
1207
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1208

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

1211
        let pair2 = a.new_pair(pair, pair).unwrap();
1✔
1212
        assert_eq!(a.sexp(pair2), SExp::Pair(pair, pair));
1✔
1213
    }
1✔
1214

1215
    #[test]
1216
    fn test_allocate_heap_limit() {
1✔
1217
        let mut a = Allocator::new_limited(6);
1✔
1218
        // we can't allocate 6 bytes
1219
        assert_eq!(a.new_atom(b"foobar").unwrap_err(), EvalErr::OutOfMemory);
1✔
1220
        // but 5 is OK
1221
        let _atom = a.new_atom(b"fooba").unwrap();
1✔
1222
    }
1✔
1223

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

1228
        for _ in 0..MAX_NUM_ATOMS - 2 {
62,499,998✔
1229
            // exhaust the number of atoms allowed to be allocated
62,499,998✔
1230
            let _ = a.new_atom(b"foo").unwrap();
62,499,998✔
1231
        }
62,499,998✔
1232
        assert_eq!(a.new_atom(b"foobar").unwrap_err(), EvalErr::TooManyAtoms);
1✔
1233
        assert_eq!(a.u8_vec.len(), 0);
1✔
1234
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1235
    }
1✔
1236

1237
    #[test]
1238
    fn test_allocate_small_number_limit() {
1✔
1239
        let mut a = Allocator::new();
1✔
1240

1241
        for _ in 0..MAX_NUM_ATOMS - 2 {
62,499,998✔
1242
            // exhaust the number of atoms allowed to be allocated
62,499,998✔
1243
            let _ = a.new_atom(b"foo").unwrap();
62,499,998✔
1244
        }
62,499,998✔
1245
        assert_eq!(a.new_small_number(3).unwrap_err(), EvalErr::TooManyAtoms);
1✔
1246
        assert_eq!(a.u8_vec.len(), 0);
1✔
1247
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1248
    }
1✔
1249

1250
    #[test]
1251
    fn test_allocate_substr_limit() {
1✔
1252
        let mut a = Allocator::new();
1✔
1253

1254
        for _ in 0..MAX_NUM_ATOMS - 3 {
62,499,997✔
1255
            // exhaust the number of atoms allowed to be allocated
62,499,997✔
1256
            let _ = a.new_atom(b"foo").unwrap();
62,499,997✔
1257
        }
62,499,997✔
1258
        let atom = a.new_atom(b"foo").unwrap();
1✔
1259
        assert_eq!(a.new_substr(atom, 1, 2).unwrap_err(), EvalErr::TooManyAtoms);
1✔
1260
        assert_eq!(a.u8_vec.len(), 0);
1✔
1261
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1262
    }
1✔
1263

1264
    #[test]
1265
    fn test_allocate_concat_limit() {
1✔
1266
        let mut a = Allocator::new();
1✔
1267

1268
        for _ in 0..MAX_NUM_ATOMS - 3 {
62,499,997✔
1269
            // exhaust the number of atoms allowed to be allocated
62,499,997✔
1270
            let _ = a.new_atom(b"foo").unwrap();
62,499,997✔
1271
        }
62,499,997✔
1272
        let atom = a.new_atom(b"foo").unwrap();
1✔
1273
        assert_eq!(a.new_concat(3, &[atom]).unwrap_err(), EvalErr::TooManyAtoms);
1✔
1274
        assert_eq!(a.u8_vec.len(), 0);
1✔
1275
        assert_eq!(a.ghost_atoms, MAX_NUM_ATOMS);
1✔
1276
    }
1✔
1277

1278
    #[test]
1279
    fn test_allocate_pair_limit() {
1✔
1280
        let mut a = Allocator::new();
1✔
1281
        let atom = a.new_atom(b"foo").unwrap();
1✔
1282
        // one pair is OK
1283
        let _pair1 = a.new_pair(atom, atom).unwrap();
1✔
1284
        for _ in 1..MAX_NUM_PAIRS {
62,500,000✔
1285
            // exhaust the number of pairs allowed to be allocated
62,499,999✔
1286
            let _ = a.new_pair(atom, atom).unwrap();
62,499,999✔
1287
        }
62,499,999✔
1288

1289
        assert_eq!(a.new_pair(atom, atom).unwrap_err(), EvalErr::TooManyPairs);
1✔
1290
        assert_eq!(a.add_ghost_pair(1).unwrap_err(), EvalErr::TooManyPairs);
1✔
1291
    }
1✔
1292

1293
    #[test]
1294
    fn test_ghost_pair_limit() {
1✔
1295
        let mut a = Allocator::new();
1✔
1296
        let atom = a.new_atom(b"foo").unwrap();
1✔
1297
        // one pair is OK
1298
        let _pair1 = a.new_pair(atom, atom).unwrap();
1✔
1299
        a.add_ghost_pair(MAX_NUM_PAIRS - 1).unwrap();
1✔
1300

1301
        assert_eq!(a.new_pair(atom, atom).unwrap_err(), EvalErr::TooManyPairs);
1✔
1302
        assert_eq!(a.add_ghost_pair(1).unwrap_err(), EvalErr::TooManyPairs);
1✔
1303
    }
1✔
1304

1305
    #[test]
1306
    fn test_substr() {
1✔
1307
        let mut a = Allocator::new();
1✔
1308
        let atom = a.new_atom(b"foobar").unwrap();
1✔
1309
        let pair = a.new_pair(atom, atom).unwrap();
1✔
1310

1311
        let sub = a.new_substr(atom, 0, 1).unwrap();
1✔
1312
        assert_eq!(a.atom(sub).as_ref(), b"f");
1✔
1313
        let sub = a.new_substr(atom, 1, 6).unwrap();
1✔
1314
        assert_eq!(a.atom(sub).as_ref(), b"oobar");
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!(matches!(
1✔
1321
            a.new_substr(atom, 1, 0).unwrap_err(),
1✔
1322
            EvalErr::InvalidAllocArg(
1323
                _,
1324
                ref msg
1✔
1325
            ) if *msg == format!("substr invalid bounds: {1} < {0}", 1, 0)
1✔
1326
        ));
1327
        assert!(matches!(
1✔
1328
            a.new_substr(atom, 7, 7).unwrap_err(),
1✔
1329
            EvalErr::InvalidAllocArg(
1330
                _,
1331
                ref msg
1✔
1332
            ) if *msg == format!("substr start out of bounds: {0} > {1}", 7, 6)
1✔
1333
        ));
1334
        assert!(matches!(
1✔
1335
            a.new_substr(atom, 0, 7).unwrap_err(),
1✔
1336
            EvalErr::InvalidAllocArg(
1337
                _,
1338
                ref msg
1✔
1339
            ) if *msg == format!("substr end out of bounds: {0} > {1}", 7, 6)
1✔
1340
        ));
1341
        assert!(matches!(
1✔
1342
            a.new_substr(atom, u32::MAX, 4).unwrap_err(),
1✔
1343
            EvalErr::InvalidAllocArg(
1344
                _,
1345
                ref msg
1✔
1346
            ) if *msg == format!("substr start out of bounds: {0} > {1}", u32::MAX, 6)
1✔
1347
        ));
1348
        assert!(matches!(
1✔
1349
            a.new_substr(pair, 0, 0).unwrap_err(),
1✔
1350
            EvalErr::InternalError(_, ref msg) if msg == "substr expected atom, got pair"
1✔
1351
        ));
1352
    }
1✔
1353

1354
    #[test]
1355
    fn test_substr_small_number() {
1✔
1356
        let mut a = Allocator::new();
1✔
1357
        let atom = a.new_atom(b"a\x80").unwrap();
1✔
1358
        assert!(a.small_number(atom).is_some());
1✔
1359

1360
        let sub = a.new_substr(atom, 0, 1).unwrap();
1✔
1361
        assert_eq!(a.atom(sub).as_ref(), b"a");
1✔
1362
        assert!(a.small_number(sub).is_some());
1✔
1363
        let sub = a.new_substr(atom, 1, 2).unwrap();
1✔
1364
        assert_eq!(a.atom(sub).as_ref(), b"\x80");
1✔
1365
        assert!(a.small_number(sub).is_none());
1✔
1366
        let sub = a.new_substr(atom, 1, 1).unwrap();
1✔
1367
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1368
        let sub = a.new_substr(atom, 0, 0).unwrap();
1✔
1369
        assert_eq!(a.atom(sub).as_ref(), b"");
1✔
1370

1371
        assert_eq!(
1✔
1372
            a.new_substr(atom, 1, 0).unwrap_err(),
1✔
1373
            EvalErr::InvalidAllocArg(atom, format!("substr invalid bounds: {1} < {0}", 1, 0))
1✔
1374
        );
1375
        assert!(matches!(
1✔
1376
            a.new_substr(atom, 3, 3).unwrap_err(),
1✔
1377
            EvalErr::InvalidAllocArg(
1378
                _,
1379
            ref msg) if *msg == format!("substr start out of bounds: {0} > {1}", 3,2 )
1✔
1380

1381
        ));
1382
        assert!(matches!(
1✔
1383
            a.new_substr(atom, 0, 3).unwrap_err(),
1✔
1384
            EvalErr::InvalidAllocArg(
1385
                _,
1386
                ref msg ) if *msg == format!("substr end out of bounds: {1} > {0}", 2,3)
1✔
1387

1388
        ));
1389
        println!("{}", a.new_substr(atom, u32::MAX, 2).unwrap_err());
1✔
1390
        assert!(matches!(
1✔
1391
            a.new_substr(atom, u32::MAX, 2).unwrap_err(),
1✔
1392
            EvalErr::InvalidAllocArg(_, ref msg) if *msg ==  format!(
1✔
1393
                    "substr start out of bounds: {0} > {1}",
1✔
1394
                    u32::MAX,
1395
                    2
1396
                )
1✔
1397
        ));
1398
    }
1✔
1399

1400
    #[test]
1401
    fn test_concat_launder_small_number() {
1✔
1402
        let mut a = Allocator::new();
1✔
1403
        let atom1 = a.new_small_number(42).expect("new_small_number");
1✔
1404
        assert_eq!(a.small_number(atom1), Some(42));
1✔
1405

1406
        // this "launders" the small number into actually being allocated on the
1407
        // heap
1408
        let atom2 = a
1✔
1409
            .new_concat(1, &[a.nil(), atom1, a.nil()])
1✔
1410
            .expect("new_substr");
1✔
1411

1412
        // even though this atom is allocated on the heap (and not stored as a small
1413
        // int), we can still retrieve it as one. The KLVM interpreter depends on
1414
        // this when matching operators against quote, apply and softfork.
1415
        assert_eq!(a.small_number(atom2), Some(42));
1✔
1416
        assert_eq!(a.atom_len(atom2), 1);
1✔
1417
        assert_eq!(a.atom(atom2).as_ref(), &[42]);
1✔
1418
    }
1✔
1419

1420
    #[test]
1421
    fn test_concat() {
1✔
1422
        let mut a = Allocator::new();
1✔
1423
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1424
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1425
        let atom3 = a.new_atom(b"o").unwrap();
1✔
1426
        let atom4 = a.new_atom(b"b").unwrap();
1✔
1427
        let atom5 = a.new_atom(b"a").unwrap();
1✔
1428
        let atom6 = a.new_atom(b"r").unwrap();
1✔
1429
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1430

1431
        let cat = a
1✔
1432
            .new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6])
1✔
1433
            .unwrap();
1✔
1434
        assert_eq!(a.atom(cat).as_ref(), b"foobar");
1✔
1435

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

1439
        assert!(matches!(
1✔
1440
            a.new_concat(11, &[cat, cat]).unwrap_err(),
1✔
1441
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1442
        ));
1443
        assert!(matches!(
1✔
1444
            a.new_concat(13, &[cat, cat]).unwrap_err(),
1✔
1445
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1446
        ));
1447
        assert!(matches!(
1✔
1448
            a.new_concat(12, &[atom3, pair]).unwrap_err(),
1✔
1449
            EvalErr::InternalError(_, ref msg) if msg == "concat expected atom, got pair"
1✔
1450
        ));
1451

1452
        assert!(matches!(
1✔
1453
            a.new_concat(4, &[atom1, atom2, atom3]).unwrap_err(),
1✔
1454
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1455
        ));
1456

1457
        assert!(matches!(
1✔
1458
            a.new_concat(2, &[atom1, atom2, atom3]).unwrap_err(),
1✔
1459
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1460
        ));
1461

1462
        assert!(matches!(
1✔
1463
            a.new_concat(2, &[atom3]).unwrap_err(),
1✔
1464
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1465
        ));
1466
        assert!(matches!(
1✔
1467
            a.new_concat(1, &[]).unwrap_err(),
1✔
1468
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1469
        ));
1470

1471
        assert_eq!(a.new_concat(0, &[]).unwrap(), NodePtr::NIL);
1✔
1472
        assert_eq!(a.new_concat(1, &[atom1]).unwrap(), atom1);
1✔
1473
    }
1✔
1474

1475
    #[test]
1476
    fn test_concat_large() {
1✔
1477
        let mut a = Allocator::new();
1✔
1478
        let atom1 = a.new_atom(b"foo").unwrap();
1✔
1479
        let atom2 = a.new_atom(b"bar").unwrap();
1✔
1480
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1481

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

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

1488
        assert!(matches!(
1✔
1489
            a.new_concat(11, &[cat, cat]).unwrap_err(),
1✔
1490
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1491
        ));
1492
        assert!(matches!(
1✔
1493
            a.new_concat(13, &[cat, cat]).unwrap_err(),
1✔
1494
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1495
        ));
1496
        assert!(matches!(
1✔
1497
            a.new_concat(12, &[atom1, pair]).unwrap_err(),
1✔
1498
            EvalErr::InternalError(_, ref msg) if msg == "concat expected atom, got pair"
1✔
1499

1500
        ));
1501

1502
        assert!(matches!(
1✔
1503
            a.new_concat(4, &[atom1, atom2]).unwrap_err(),
1✔
1504
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1505
        ));
1506

1507
        assert!(matches!(
1✔
1508
            a.new_concat(2, &[atom1, atom2]).unwrap_err(),
1✔
1509
            EvalErr::InternalError(_, ref msg) if msg == "concat passed invalid new_size"
1✔
1510
        ));
1511
    }
1✔
1512

1513
    #[test]
1514
    fn test_sexp() {
1✔
1515
        let mut a = Allocator::new();
1✔
1516
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1517
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1518
        let pair = a.new_pair(atom1, atom2).unwrap();
1✔
1519

1520
        assert_eq!(a.sexp(atom1), SExp::Atom);
1✔
1521
        assert_eq!(a.sexp(atom2), SExp::Atom);
1✔
1522
        assert_eq!(a.sexp(pair), SExp::Pair(atom1, atom2));
1✔
1523
    }
1✔
1524

1525
    #[test]
1526
    fn test_concat_limit() {
1✔
1527
        let mut a = Allocator::new_limited(6);
1✔
1528
        let atom1 = a.new_atom(b"f").unwrap();
1✔
1529
        let atom2 = a.new_atom(b"o").unwrap();
1✔
1530
        let atom3 = a.new_atom(b"o").unwrap();
1✔
1531
        let atom4 = a.new_atom(b"b").unwrap();
1✔
1532
        let atom5 = a.new_atom(b"a").unwrap();
1✔
1533
        let atom6 = a.new_atom(b"r").unwrap();
1✔
1534

1535
        // we only have 2 bytes left of allowed heap allocation
1536
        assert_eq!(
1✔
1537
            a.new_concat(6, &[atom1, atom2, atom3, atom4, atom5, atom6])
1✔
1538
                .unwrap_err(),
1✔
1539
            EvalErr::OutOfMemory
1540
        );
1541
        let cat = a.new_concat(2, &[atom1, atom2]).unwrap();
1✔
1542
        assert_eq!(a.atom(cat).as_ref(), b"fo");
1✔
1543
    }
1✔
1544

1545
    #[rstest]
1546
    #[case(0.into(), &[])]
1547
    #[case(1.into(), &[1])]
1548
    #[case((-1).into(), &[0xff])]
1549
    #[case(0x80.into(), &[0, 0x80])]
1550
    #[case(0xff.into(), &[0, 0xff])]
1551
    #[case(0xffffffff_u64.into(), &[0, 0xff, 0xff, 0xff, 0xff])]
1552
    fn test_new_number(#[case] num: Number, #[case] expected: &[u8]) {
1553
        let mut a = Allocator::new();
1554

1555
        // TEST creating the atom from a Number
1556
        let atom = a.new_number(num.clone()).unwrap();
1557

1558
        // make sure we get back the same number
1559
        assert_eq!(a.number(atom), num);
1560
        assert_eq!(a.atom(atom).as_ref(), expected);
1561
        assert_eq!(number_from_u8(expected), num);
1562

1563
        // TEST creating the atom from a buffer
1564
        let atom = a.new_atom(expected).unwrap();
1565

1566
        // make sure we get back the same number
1567
        assert_eq!(a.number(atom), num);
1568
        assert_eq!(a.atom(atom).as_ref(), expected);
1569
        assert_eq!(number_from_u8(expected), num);
1570
    }
1571

1572
    #[test]
1573
    fn test_checkpoints() {
1✔
1574
        let mut a = Allocator::new();
1✔
1575

1576
        let atom1 = a.new_atom(&[4, 3, 2, 1]).unwrap();
1✔
1577
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1578

1579
        let checkpoint = a.checkpoint();
1✔
1580

1581
        let atom2 = a.new_atom(&[6, 5, 4, 3]).unwrap();
1✔
1582
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1583
        assert!(a.atom(atom2).as_ref() == [6, 5, 4, 3]);
1✔
1584

1585
        // at this point we have two atoms and a checkpoint from before the second
1586
        // atom was created
1587

1588
        // now, restoring the checkpoint state will make atom2 disappear
1589

1590
        a.restore_checkpoint(&checkpoint);
1✔
1591

1592
        assert!(a.atom(atom1).as_ref() == [4, 3, 2, 1]);
1✔
1593
        let atom3 = a.new_atom(&[6, 5, 4, 3]).unwrap();
1✔
1594
        assert!(a.atom(atom3).as_ref() == [6, 5, 4, 3]);
1✔
1595

1596
        // since atom2 was removed, atom3 should actually be using that slot
1597
        assert_eq!(atom2, atom3);
1✔
1598
    }
1✔
1599

1600
    fn test_g1(a: &Allocator, n: NodePtr) -> EvalErr {
6✔
1601
        a.g1(n).unwrap_err()
6✔
1602
    }
6✔
1603

1604
    fn test_g2(a: &Allocator, n: NodePtr) -> EvalErr {
6✔
1605
        a.g2(n).unwrap_err()
6✔
1606
    }
6✔
1607

1608
    type TestFun = fn(&Allocator, NodePtr) -> EvalErr;
1609

1610
    #[rstest]
1611
    #[case(test_g1, 0, "atom is not G1 size, 48 bytes")]
1612
    #[case(test_g1, 3, "atom is not G1 size, 48 bytes")]
1613
    #[case(test_g1, 47, "atom is not G1 size, 48 bytes")]
1614
    #[case(test_g1, 49, "atom is not G1 size, 48 bytes")]
1615
    #[case(test_g1, 48, "atom is not a G1 point")]
1616
    #[case(test_g2, 0, "atom is not G2 size, 96 bytes")]
1617
    #[case(test_g2, 3, "atom is not G2 size, 96 bytes")]
1618
    #[case(test_g2, 95, "atom is not G2 size, 96 bytes")]
1619
    #[case(test_g2, 97, "atom is not G2 size, 96 bytes")]
1620
    #[case(test_g2, 96, "atom is not a G2 point")]
1621
    fn test_point_size_error(#[case] fun: TestFun, #[case] size: usize, #[case] expected: &str) {
1622
        let mut a = Allocator::new();
1623
        let mut buf = Vec::<u8>::new();
1624
        buf.resize(size, 0xcc);
1625
        let n = a.new_atom(&buf).unwrap();
1626
        let r = fun(&a, n);
1627
        assert_eq!(r.to_string(), format!("InvalidAllocatorArg: {expected}"));
1628
    }
1629

1630
    #[rstest]
1631
    #[case(test_g1, "pair found, expected G1 point")]
1632
    #[case(test_g2, "pair found, expected G2 point")]
1633
    fn test_point_atom_pair(#[case] fun: TestFun, #[case] expected: &str) {
1634
        let mut a = Allocator::new();
1635
        let n = a.new_pair(a.nil(), a.one()).unwrap();
1636
        let r = fun(&a, n);
1637
        assert_eq!(r.to_string(), format!("InvalidAllocatorArg: {expected}"));
1638
    }
1639

1640
    #[rstest]
1641
    #[case(
1642
        "\
1643
97f1d3a73197d7942695638c4fa9ac0f\
1644
c3688c4f9774b905a14e3a3f171bac58\
1645
6c55e83ff97a1aeffb3af00adb22c6bb"
1646
    )]
1647
    #[case(
1648
        "\
1649
a572cbea904d67468808c8eb50a9450c\
1650
9721db309128012543902d0ac358a62a\
1651
e28f75bb8f1c7c42c39a8c5529bf0f4e"
1652
    )]
1653
    fn test_g1_roundtrip(#[case] atom: &str) {
1654
        let mut a = Allocator::new();
1655
        let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap();
1656
        let g1 = a.g1(n).unwrap();
1657
        assert_eq!(hex::encode(g1.to_bytes()), atom);
1658

1659
        let g1_copy = a.new_g1(g1).unwrap();
1660
        let g1_atom = a.atom(g1_copy);
1661
        assert_eq!(hex::encode(g1_atom), atom);
1662

1663
        // try interpreting the point as G1
1664
        assert!(matches!(
1665
            a.g2(n).unwrap_err(),
1666
            EvalErr::InvalidAllocArg(_, msg) if msg == "atom is not G2 size, 96 bytes"
1667
        ));
1668
        assert!(matches!(
1669
            a.g2(g1_copy).unwrap_err(),
1670
            EvalErr::InvalidAllocArg(_, msg) if msg == "atom is not G2 size, 96 bytes"
1671
        ));
1672

1673
        // try interpreting the point as number
1674
        assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap()));
1675
        assert_eq!(
1676
            a.number(g1_copy),
1677
            number_from_u8(&hex::decode(atom).unwrap())
1678
        );
1679
    }
1680

1681
    #[rstest]
1682
    #[case(
1683
        "\
1684
93e02b6052719f607dacd3a088274f65\
1685
596bd0d09920b61ab5da61bbdc7f5049\
1686
334cf11213945d57e5ac7d055d042b7e\
1687
024aa2b2f08f0a91260805272dc51051\
1688
c6e47ad4fa403b02b4510b647ae3d177\
1689
0bac0326a805bbefd48056c8c121bdb8"
1690
    )]
1691
    #[case(
1692
        "\
1693
aa4edef9c1ed7f729f520e47730a124f\
1694
d70662a904ba1074728114d1031e1572\
1695
c6c886f6b57ec72a6178288c47c33577\
1696
1638533957d540a9d2370f17cc7ed586\
1697
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1698
81f14b0bf3611b78c952aacab827a053"
1699
    )]
1700
    fn test_g2_roundtrip(#[case] atom: &str) {
1701
        let mut a = Allocator::new();
1702
        let n = a.new_atom(&hex::decode(atom).unwrap()).unwrap();
1703
        let g2 = a.g2(n).unwrap();
1704
        assert_eq!(hex::encode(g2.to_bytes()), atom);
1705

1706
        let g2_copy = a.new_g2(g2).unwrap();
1707
        let g2_atom = a.atom(g2_copy);
1708
        assert_eq!(hex::encode(g2_atom), atom);
1709

1710
        // try interpreting the point as G1
1711
        assert!(matches!(
1712
            a.g1(n).unwrap_err(),
1713
            EvalErr::InvalidAllocArg(_, msg) if msg == "atom is not G1 size, 48 bytes"
1714
        ));
1715
        assert!(matches!(
1716
            a.g1(g2_copy).unwrap_err(),
1717
            EvalErr::InvalidAllocArg(_, msg) if msg == "atom is not G1 size, 48 bytes"
1718
        ));
1719

1720
        // try interpreting the point as number
1721
        assert_eq!(a.number(n), number_from_u8(&hex::decode(atom).unwrap()));
1722
        assert_eq!(
1723
            a.number(g2_copy),
1724
            number_from_u8(&hex::decode(atom).unwrap())
1725
        );
1726
    }
1727

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

1730
    fn make_buf(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
16✔
1731
        a.new_atom(bytes).unwrap()
16✔
1732
    }
16✔
1733

1734
    fn make_number(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
16✔
1735
        let v = number_from_u8(bytes);
16✔
1736
        a.new_number(v).unwrap()
16✔
1737
    }
16✔
1738

1739
    fn make_g1(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
4✔
1740
        let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1741
        a.new_g1(v).unwrap()
4✔
1742
    }
4✔
1743

1744
    fn make_g2(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
4✔
1745
        let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1746
        a.new_g2(v).unwrap()
4✔
1747
    }
4✔
1748

1749
    fn make_g1_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
12✔
1750
        assert!(<[u8; 48]>::try_from(bytes).is_err());
12✔
1751
        a.new_atom(bytes).unwrap()
12✔
1752
    }
12✔
1753

1754
    fn make_g2_fail(a: &mut Allocator, bytes: &[u8]) -> NodePtr {
12✔
1755
        assert!(<[u8; 96]>::try_from(bytes).is_err());
12✔
1756
        a.new_atom(bytes).unwrap()
12✔
1757
    }
12✔
1758

1759
    type CheckFun = fn(&Allocator, NodePtr, &[u8]);
1760

1761
    fn check_buf(a: &Allocator, n: NodePtr, bytes: &[u8]) {
16✔
1762
        let buf = a.atom(n);
16✔
1763
        assert_eq!(buf.as_ref(), bytes);
16✔
1764
    }
16✔
1765

1766
    fn check_number(a: &Allocator, n: NodePtr, bytes: &[u8]) {
16✔
1767
        let num = a.number(n);
16✔
1768
        let v = number_from_u8(bytes);
16✔
1769
        assert_eq!(num, v);
16✔
1770
    }
16✔
1771

1772
    fn check_g1(a: &Allocator, n: NodePtr, bytes: &[u8]) {
4✔
1773
        let num = a.g1(n).unwrap();
4✔
1774
        let v = G1Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1775
        assert_eq!(num, v);
4✔
1776
    }
4✔
1777

1778
    fn check_g2(a: &Allocator, n: NodePtr, bytes: &[u8]) {
4✔
1779
        let num = a.g2(n).unwrap();
4✔
1780
        let v = G2Element::from_bytes(bytes.try_into().unwrap()).unwrap();
4✔
1781
        assert_eq!(num, v);
4✔
1782
    }
4✔
1783

1784
    fn check_g1_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) {
12✔
1785
        assert_eq!(a.g1(n).unwrap_err().node_ptr(), n);
12✔
1786
        assert!(<[u8; 48]>::try_from(bytes).is_err());
12✔
1787
    }
12✔
1788

1789
    fn check_g2_fail(a: &Allocator, n: NodePtr, bytes: &[u8]) {
12✔
1790
        assert_eq!(a.g2(n).unwrap_err().node_ptr(), n);
12✔
1791
        assert!(<[u8; 96]>::try_from(bytes).is_err());
12✔
1792
    }
12✔
1793

1794
    const EMPTY: &str = "";
1795

1796
    const SMALL_BUF: &str = "133742";
1797

1798
    const VALID_G1: &str = "\
1799
a572cbea904d67468808c8eb50a9450c\
1800
9721db309128012543902d0ac358a62a\
1801
e28f75bb8f1c7c42c39a8c5529bf0f4e";
1802

1803
    const VALID_G2: &str = "\
1804
aa4edef9c1ed7f729f520e47730a124f\
1805
d70662a904ba1074728114d1031e1572\
1806
c6c886f6b57ec72a6178288c47c33577\
1807
1638533957d540a9d2370f17cc7ed586\
1808
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1809
81f14b0bf3611b78c952aacab827a053";
1810

1811
    /*
1812
      We want to exercise round-tripping avery kind of value via every other kind
1813
      of value (as far as possible). e.g. Every value can round-trip through a byte buffer
1814
      or a number, but G1 cannot round-trip via G2.
1815

1816
      +-----------+--------+--------+------+------+
1817
      | from / to | buffer | number | G1   | G2   |
1818
      +-----------+--------+--------+------+------+
1819
      | buffer    | o      | o      | -    | -    |
1820
      | number    | o      | o      | -    | -    |
1821
      | G1        | o      | o      | o    | -    |
1822
      | G2        | o      | o      | -    | o    |
1823
      +-----------+--------+--------+------+------+
1824

1825
    */
1826

1827
    #[rstest]
1828
    // round trip empty buffer
1829
    #[case(EMPTY, make_buf, check_buf)]
1830
    #[case(EMPTY, make_buf, check_number)]
1831
    #[case(EMPTY, make_buf, check_g1_fail)]
1832
    #[case(EMPTY, make_buf, check_g2_fail)]
1833
    #[case(EMPTY, make_number, check_buf)]
1834
    #[case(EMPTY, make_number, check_number)]
1835
    #[case(EMPTY, make_number, check_g1_fail)]
1836
    #[case(EMPTY, make_number, check_g2_fail)]
1837
    #[case(EMPTY, make_g1_fail, check_buf)]
1838
    #[case(EMPTY, make_g1_fail, check_number)]
1839
    #[case(EMPTY, make_g1_fail, check_g1_fail)]
1840
    #[case(EMPTY, make_g1_fail, check_g2_fail)]
1841
    #[case(EMPTY, make_g2_fail, check_buf)]
1842
    #[case(EMPTY, make_g2_fail, check_number)]
1843
    #[case(EMPTY, make_g2_fail, check_g1_fail)]
1844
    #[case(EMPTY, make_g2_fail, check_g2_fail)]
1845
    // round trip small buffer
1846
    #[case(SMALL_BUF, make_buf, check_buf)]
1847
    #[case(SMALL_BUF, make_buf, check_number)]
1848
    #[case(SMALL_BUF, make_buf, check_g1_fail)]
1849
    #[case(SMALL_BUF, make_buf, check_g2_fail)]
1850
    #[case(SMALL_BUF, make_number, check_buf)]
1851
    #[case(SMALL_BUF, make_number, check_number)]
1852
    #[case(SMALL_BUF, make_number, check_g1_fail)]
1853
    #[case(SMALL_BUF, make_number, check_g2_fail)]
1854
    #[case(SMALL_BUF, make_g1_fail, check_buf)]
1855
    #[case(SMALL_BUF, make_g1_fail, check_number)]
1856
    #[case(SMALL_BUF, make_g1_fail, check_g1_fail)]
1857
    #[case(SMALL_BUF, make_g1_fail, check_g2_fail)]
1858
    #[case(SMALL_BUF, make_g2_fail, check_buf)]
1859
    #[case(SMALL_BUF, make_g2_fail, check_number)]
1860
    #[case(SMALL_BUF, make_g2_fail, check_g1_fail)]
1861
    #[case(SMALL_BUF, make_g2_fail, check_g2_fail)]
1862
    // round trip G1 point
1863
    #[case(VALID_G1, make_buf, check_buf)]
1864
    #[case(VALID_G1, make_buf, check_number)]
1865
    #[case(VALID_G1, make_buf, check_g1)]
1866
    #[case(VALID_G1, make_buf, check_g2_fail)]
1867
    #[case(VALID_G1, make_number, check_buf)]
1868
    #[case(VALID_G1, make_number, check_number)]
1869
    #[case(VALID_G1, make_number, check_g1)]
1870
    #[case(VALID_G1, make_number, check_g2_fail)]
1871
    #[case(VALID_G1, make_g1, check_buf)]
1872
    #[case(VALID_G1, make_g1, check_number)]
1873
    #[case(VALID_G1, make_g1, check_g1)]
1874
    #[case(VALID_G1, make_g1, check_g2_fail)]
1875
    #[case(VALID_G1, make_g2_fail, check_buf)]
1876
    #[case(VALID_G1, make_g2_fail, check_number)]
1877
    #[case(VALID_G1, make_g2_fail, check_g1)]
1878
    #[case(VALID_G1, make_g2_fail, check_g2_fail)]
1879
    // round trip G2 point
1880
    #[case(VALID_G2, make_buf, check_buf)]
1881
    #[case(VALID_G2, make_buf, check_number)]
1882
    #[case(VALID_G2, make_buf, check_g1_fail)]
1883
    #[case(VALID_G2, make_buf, check_g2)]
1884
    #[case(VALID_G2, make_number, check_buf)]
1885
    #[case(VALID_G2, make_number, check_number)]
1886
    #[case(VALID_G2, make_number, check_g1_fail)]
1887
    #[case(VALID_G2, make_number, check_g2)]
1888
    #[case(VALID_G2, make_g1_fail, check_buf)]
1889
    #[case(VALID_G2, make_g1_fail, check_number)]
1890
    #[case(VALID_G2, make_g1_fail, check_g1_fail)]
1891
    #[case(VALID_G2, make_g1_fail, check_g2)]
1892
    #[case(VALID_G2, make_g2, check_buf)]
1893
    #[case(VALID_G2, make_g2, check_number)]
1894
    #[case(VALID_G2, make_g2, check_g1_fail)]
1895
    #[case(VALID_G2, make_g2, check_g2)]
1896
    fn test_roundtrip(#[case] test_value: &str, #[case] make: MakeFun, #[case] check: CheckFun) {
1897
        let value = hex::decode(test_value).unwrap();
1898
        let mut a = Allocator::new();
1899
        let node = make(&mut a, &value);
1900
        check(&a, node, &value);
1901
    }
1902

1903
    #[rstest]
1904
    #[case(&[], 0)]
1905
    #[case(&[1], 1)]
1906
    #[case(&[1,2], 2)]
1907
    #[case(&[1,2,3,4,5,6,7,8,9], 9)]
1908
    #[case(&[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], 18)]
1909
    fn test_atom_len(#[case] buf: &[u8], #[case] expected: usize) {
1910
        let mut a = Allocator::new();
1911
        let atom = a.new_atom(buf).unwrap();
1912
        assert_eq!(a.atom_len(atom), expected);
1913
    }
1914

1915
    #[rstest]
1916
    #[case(0.into(), 0)]
1917
    #[case(42.into(), 1)]
1918
    #[case(127.into(), 1)]
1919
    #[case(1337.into(), 2)]
1920
    #[case(0x7fffff.into(), 3)]
1921
    #[case(0xffffff.into(), 4)]
1922
    #[case((-1).into(), 1)]
1923
    #[case((-128).into(), 1)]
1924
    fn test_atom_len_number(#[case] value: Number, #[case] expected: usize) {
1925
        let mut a = Allocator::new();
1926
        let atom = a.new_number(value).unwrap();
1927
        assert_eq!(a.atom_len(atom), expected);
1928
    }
1929

1930
    #[rstest]
1931
    #[case(
1932
        "\
1933
97f1d3a73197d7942695638c4fa9ac0f\
1934
c3688c4f9774b905a14e3a3f171bac58\
1935
6c55e83ff97a1aeffb3af00adb22c6bb",
1936
        48
1937
    )]
1938
    #[case(
1939
        "\
1940
a572cbea904d67468808c8eb50a9450c\
1941
9721db309128012543902d0ac358a62a\
1942
e28f75bb8f1c7c42c39a8c5529bf0f4e",
1943
        48
1944
    )]
1945
    fn test_atom_len_g1(#[case] buffer_hex: &str, #[case] expected: usize) {
1946
        let mut a = Allocator::new();
1947
        let buffer = &hex::decode(buffer_hex).unwrap();
1948
        let g1 = G1Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G1 point");
1949
        let atom = a.new_g1(g1).unwrap();
1950
        assert_eq!(a.atom_len(atom), expected);
1951
    }
1952

1953
    #[rstest]
1954
    #[case(
1955
        "\
1956
93e02b6052719f607dacd3a088274f65\
1957
596bd0d09920b61ab5da61bbdc7f5049\
1958
334cf11213945d57e5ac7d055d042b7e\
1959
024aa2b2f08f0a91260805272dc51051\
1960
c6e47ad4fa403b02b4510b647ae3d177\
1961
0bac0326a805bbefd48056c8c121bdb8",
1962
        96
1963
    )]
1964
    #[case(
1965
        "\
1966
aa4edef9c1ed7f729f520e47730a124f\
1967
d70662a904ba1074728114d1031e1572\
1968
c6c886f6b57ec72a6178288c47c33577\
1969
1638533957d540a9d2370f17cc7ed586\
1970
3bc0b995b8825e0ee1ea1e1e4d00dbae\
1971
81f14b0bf3611b78c952aacab827a053",
1972
        96
1973
    )]
1974
    fn test_atom_len_g2(#[case] buffer_hex: &str, #[case] expected: usize) {
1975
        let mut a = Allocator::new();
1976

1977
        let buffer = &hex::decode(buffer_hex).unwrap();
1978
        let g2 = G2Element::from_bytes(&buffer[..].try_into().unwrap()).expect("invalid G2 point");
1979
        let atom = a.new_g2(g2).unwrap();
1980
        assert_eq!(a.atom_len(atom), expected);
1981
    }
1982

1983
    #[rstest]
1984
    #[case(0.into())]
1985
    #[case(1.into())]
1986
    #[case(0x7f.into())]
1987
    #[case(0x80.into())]
1988
    #[case(0xff.into())]
1989
    #[case(0x100.into())]
1990
    #[case(0x7fff.into())]
1991
    #[case(0x8000.into())]
1992
    #[case(0xffff.into())]
1993
    #[case(0x10000.into())]
1994
    #[case(0x7ffff.into())]
1995
    #[case(0x80000.into())]
1996
    #[case(0xfffff.into())]
1997
    #[case(0x100000.into())]
1998
    #[case(0x7ffffff.into())]
1999
    #[case(0x8000000.into())]
2000
    #[case(0xfffffff.into())]
2001
    #[case(0x10000000.into())]
2002
    #[case(0x7ffffffff_u64.into())]
2003
    #[case(0x8000000000_u64.into())]
2004
    #[case(0xffffffffff_u64.into())]
2005
    #[case(0x10000000000_u64.into())]
2006
    #[case((-1).into())]
2007
    #[case((-0x7f).into())]
2008
    #[case((-0x80).into())]
2009
    #[case((-0xff).into())]
2010
    #[case((-0x100).into())]
2011
    #[case((-0x7fff).into())]
2012
    #[case((-0x8000).into())]
2013
    #[case((-0xffff).into())]
2014
    #[case((-0x10000).into())]
2015
    #[case((-0x7ffff).into())]
2016
    #[case((-0x80000).into())]
2017
    #[case((-0xfffff).into())]
2018
    #[case((-0x100000).into())]
2019
    #[case((-0x7ffffff_i64).into())]
2020
    #[case((-0x8000000_i64).into())]
2021
    #[case((-0xfffffff_i64).into())]
2022
    #[case((-0x10000000_i64).into())]
2023
    #[case((-0x7ffffffff_i64).into())]
2024
    #[case((-0x8000000000_i64).into())]
2025
    #[case((-0xffffffffff_i64).into())]
2026
    #[case((-0x10000000000_i64).into())]
2027
    fn test_number_roundtrip(#[case] value: Number) {
2028
        let mut a = Allocator::new();
2029
        let atom = a.new_number(value.clone()).expect("new_number()");
2030
        assert_eq!(a.number(atom), value);
2031
    }
2032

2033
    #[rstest]
2034
    #[case(0)]
2035
    #[case(1)]
2036
    #[case(0x7f)]
2037
    #[case(0x80)]
2038
    #[case(0xff)]
2039
    #[case(0x100)]
2040
    #[case(0x7fff)]
2041
    #[case(0x8000)]
2042
    #[case(0xffff)]
2043
    #[case(0x10000)]
2044
    #[case(0x7ffff)]
2045
    #[case(0x80000)]
2046
    #[case(0xfffff)]
2047
    #[case(0x100000)]
2048
    #[case(0x7fffff)]
2049
    #[case(0x800000)]
2050
    #[case(0xffffff)]
2051
    #[case(0x1000000)]
2052
    #[case(0x3ffffff)]
2053
    fn test_small_number_roundtrip(#[case] value: u32) {
2054
        let mut a = Allocator::new();
2055
        let atom = a.new_small_number(value).expect("new_small_number()");
2056
        assert_eq!(a.small_number(atom).expect("small_number()"), value);
2057
    }
2058

2059
    #[rstest]
2060
    #[case(0.into(), true)]
2061
    #[case(1.into(), true)]
2062
    #[case(0x3ffffff.into(), true)]
2063
    #[case(0x4000000.into(), false)]
2064
    #[case(0x7f.into(), true)]
2065
    #[case(0x80.into(), true)]
2066
    #[case(0xff.into(), true)]
2067
    #[case(0x100.into(), true)]
2068
    #[case(0x7fff.into(), true)]
2069
    #[case(0x8000.into(), true)]
2070
    #[case(0xffff.into(), true)]
2071
    #[case(0x10000.into(), true)]
2072
    #[case(0x7ffff.into(), true)]
2073
    #[case(0x80000.into(), true)]
2074
    #[case(0xfffff.into(), true)]
2075
    #[case(0x100000.into(), true)]
2076
    #[case(0x7ffffff.into(), false)]
2077
    #[case(0x8000000.into(), false)]
2078
    #[case(0xfffffff.into(), false)]
2079
    #[case(0x10000000.into(), false)]
2080
    #[case(0x7ffffffff_u64.into(), false)]
2081
    #[case(0x8000000000_u64.into(), false )]
2082
    #[case(0xffffffffff_u64.into(), false)]
2083
    #[case(0x10000000000_u64.into(), false)]
2084
    #[case((-1).into(), false)]
2085
    #[case((-0x7f).into(), false)]
2086
    #[case((-0x80).into(), false)]
2087
    #[case((-0x10000000000_i64).into(), false)]
2088
    fn test_auto_small_number(#[case] value: Number, #[case] expect_small: bool) {
2089
        let mut a = Allocator::new();
2090
        let atom = a.new_number(value.clone()).expect("new_number()");
2091
        assert_eq!(a.small_number(atom).is_some(), expect_small);
2092
        if let Some(v) = a.small_number(atom) {
2093
            use num_traits::ToPrimitive;
2094
            assert_eq!(v, value.to_u32().unwrap());
2095
        }
2096
        assert_eq!(a.number(atom), value);
2097
    }
2098

2099
    #[rstest]
2100
    // redundant leading zeros are not canoncial
2101
    #[case(&[0x00], false)]
2102
    #[case(&[0x00, 0x7f], false)]
2103
    // negative numbers cannot be small ints
2104
    #[case(&[0x80], false)]
2105
    #[case(&[0xff], false)]
2106
    #[case(&[0xff, 0xff], false)]
2107
    #[case(&[0x80, 0xff, 0xff], false)]
2108
    // small positive intergers can be small
2109
    #[case(&[0x01], true)]
2110
    #[case(&[0x00, 0xff], true)]
2111
    #[case(&[0x7f, 0xff], true)]
2112
    #[case(&[0x7f, 0xff, 0xff], true)]
2113
    #[case(&[0x00, 0xff, 0xff, 0xff], true)]
2114
    #[case(&[0x02, 0x00, 0x00, 0x00], true)]
2115
    #[case(&[0x03, 0xff, 0xff, 0xff], true)]
2116
    // too big
2117
    #[case(&[0x04, 0x00, 0x00, 0x00], false)]
2118
    fn test_auto_small_number_from_buf(#[case] buf: &[u8], #[case] expect_small: bool) {
2119
        let mut a = Allocator::new();
2120
        let atom = a.new_atom(buf).expect("new_atom()");
2121
        assert_eq!(a.small_number(atom).is_some(), expect_small);
2122
        if let Some(v) = a.small_number(atom) {
2123
            use num_traits::ToPrimitive;
2124
            assert_eq!(v, a.number(atom).to_u32().expect("to_u32()"));
2125
        }
2126
        assert_eq!(buf, a.atom(atom).as_ref());
2127
    }
2128

2129
    #[rstest]
2130
    // redundant leading zeros are not canoncial
2131
    #[case(&[0x00], None)]
2132
    #[case(&[0x00, 0x7f], None)]
2133
    // negative numbers cannot be small ints
2134
    #[case(&[0x80], None)]
2135
    #[case(&[0xff], None)]
2136
    // redundant leading 0xff are still negative
2137
    #[case(&[0xff, 0xff], None)]
2138
    #[case(&[0x80, 0xff, 0xff], None)]
2139
    // to big
2140
    #[case(&[0x04, 0x00, 0x00, 0x00], None)]
2141
    #[case(&[0x05, 0x00, 0x00, 0x00], None)]
2142
    #[case(&[0x04, 0x00, 0x00, 0x00, 0x00], None)]
2143
    // small positive intergers can be small
2144
    #[case(&[0x01], Some(0x01))]
2145
    #[case(&[0x00, 0x80], Some(0x80))]
2146
    #[case(&[0x00, 0xff], Some(0xff))]
2147
    #[case(&[0x7f, 0xff], Some(0x7fff))]
2148
    #[case(&[0x00, 0x80, 0x00], Some(0x8000))]
2149
    #[case(&[0x00, 0xff, 0xff], Some(0xffff))]
2150
    #[case(&[0x7f, 0xff, 0xff], Some(0x7fffff))]
2151
    #[case(&[0x00, 0x80, 0x00, 0x00], Some(0x800000))]
2152
    #[case(&[0x00, 0xff, 0xff, 0xff], Some(0xffffff))]
2153
    #[case(&[0x02, 0x00, 0x00, 0x00], Some(0x2000000))]
2154
    #[case(&[0x03, 0x00, 0x00, 0x00], Some(0x3000000))]
2155
    #[case(&[0x03, 0xff, 0xff, 0xff], Some(0x3ffffff))]
2156
    fn test_fits_in_small_atom(#[case] buf: &[u8], #[case] expected: Option<u32>) {
2157
        assert_eq!(fits_in_small_atom(buf), expected);
2158
    }
2159

2160
    #[rstest]
2161
    // 0 is encoded as an empty string
2162
    #[case(&[0], "0", &[])]
2163
    #[case(&[1], "1", &[1])]
2164
    // leading zeroes are redundant
2165
    #[case(&[0,0,0,1], "1", &[1])]
2166
    #[case(&[0,0,0x80], "128", &[0, 0x80])]
2167
    // A leading zero is necessary to encode a positive number with the
2168
    // penultimate byte's most significant bit set
2169
    #[case(&[0,0xff], "255", &[0, 0xff])]
2170
    #[case(&[0x7f,0xff], "32767", &[0x7f, 0xff])]
2171
    // the first byte is redundant, it's still -1
2172
    #[case(&[0xff,0xff], "-1", &[0xff])]
2173
    #[case(&[0xff], "-1", &[0xff])]
2174
    #[case(&[0,0,0x80,0], "32768", &[0,0x80,0])]
2175
    #[case(&[0,0,0x40,0], "16384", &[0x40,0])]
2176
    fn test_number_to_atom(#[case] bytes: &[u8], #[case] text: &str, #[case] buf: &[u8]) {
2177
        let mut a = Allocator::new();
2178

2179
        // 0 is encoded as an empty string
2180
        let num = number_from_u8(bytes);
2181
        assert_eq!(format!("{num}"), text);
2182
        let ptr = a.new_number(num).unwrap();
2183
        assert_eq!(a.atom(ptr).as_ref(), buf);
2184
    }
2185
}
2186

2187
#[cfg(feature = "allocator-debug")]
2188
#[cfg(test)]
2189
mod debug_tests {
2190
    use super::*;
2191
    use chik_bls::PublicKey;
2192
    use chik_bls::Signature;
2193
    use rstest::rstest;
2194

2195
    fn new_node(a: &mut Allocator, case: u8) -> (NodePtr, usize) {
2196
        match case {
2197
            0 => (a.nil(), 0),
2198
            1 => (a.one(), 1),
2199
            2 => (a.new_atom(b"foobar").expect("new_atom"), 6),
2200
            3 => (a.new_pair(NodePtr::NIL, NodePtr::NIL).expect("new_pair"), 0),
2201
            4 => (a.new_concat(2, &[a.one(), a.one()]).expect("new_concat"), 2),
2202
            5 => (a.new_substr(a.one(), 0, 1).expect("new_substr"), 1),
2203
            6 => (a.new_small_number(1337).expect("new_small_number"), 2),
2204
            7 => (a.new_number(u32::MAX.into()).expect("new_number"), 5),
2205
            8 => (a.new_g1(PublicKey::default()).expect("new_g1"), 48),
2206
            9 => (a.new_g2(Signature::default()).expect("new_g2"), 32),
2207
            _ => {
2208
                panic!("unexpected case");
2209
            }
2210
        }
2211
    }
2212

2213
    fn access_node(a: &mut Allocator, n: NodePtr, len: usize, case: u8) {
2214
        match case {
2215
            0 => {
2216
                let _ = a.new_pair(n, a.nil());
2217
            }
2218
            1 => {
2219
                let _ = a.new_pair(a.nil(), n);
2220
            }
2221
            2 => {
2222
                let _ = a.new_substr(n, 0, (len / 2) as u32);
2223
            }
2224
            3 => {
2225
                let _ = a.new_concat(len, &[n]);
2226
            }
2227
            4 => {
2228
                let _ = a.new_concat(len + 1, &[a.one(), n]);
2229
            }
2230
            5 => {
2231
                a.atom_eq(a.one(), n);
2232
            }
2233
            6 => {
2234
                a.atom_eq(n, a.one());
2235
            }
2236
            7 => {
2237
                a.atom(n);
2238
            }
2239
            8 => {
2240
                a.atom_len(n);
2241
            }
2242
            9 => {
2243
                a.small_number(n);
2244
            }
2245
            10 => {
2246
                a.number(n);
2247
            }
2248
            11 => {
2249
                let _ = a.g1(n);
2250
            }
2251
            12 => {
2252
                let _ = a.g2(n);
2253
            }
2254
            13 => {
2255
                a.node(n);
2256
            }
2257
            14 => {
2258
                a.sexp(n);
2259
            }
2260
            15 => {
2261
                a.next(n);
2262
            }
2263
            _ => {
2264
                panic!("unexpected case");
2265
            }
2266
        }
2267
    }
2268

2269
    #[rstest]
2270
    #[should_panic(expected = "using a NodePtr on the wrong Allocator")]
2271
    fn mixing_allocators(
2272
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)] create_case: u8,
2273
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] access_case: u8,
2274
    ) {
2275
        let mut a1 = Allocator::new();
2276
        let mut a2 = Allocator::new();
2277

2278
        let (node, len) = new_node(&mut a1, create_case);
2279
        access_node(&mut a2, node, len, access_case);
2280
    }
2281

2282
    #[rstest]
2283
    #[should_panic(expected = "was invalidated by restore_checkpoint()")]
2284
    fn invalidating_node(
2285
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)] create_case: u8,
2286
        #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] access_case: u8,
2287
    ) {
2288
        let mut a1 = Allocator::new();
2289
        let checkpoint = a1.checkpoint();
2290

2291
        let (node, len) = new_node(&mut a1, create_case);
2292

2293
        // this will invalidate "node"
2294
        a1.restore_checkpoint(&checkpoint);
2295
        access_node(&mut a1, node, len, access_case);
2296

2297
        if matches!(a1.node(node), NodeVisitor::U32(_)) {
2298
            // small atoms aren't allocated on the heap, so we don't know
2299
            // whether they should have been invalidated or not. They never are
2300
            // in practice.
2301
            panic!("simulated NodePtr was invalidated by restore_checkpoint()");
2302
        }
2303
    }
2304
}
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