1
use std::fmt;
2

            
3
use merc_macros::merc_derive_terms;
4

            
5
use crate::Symb;
6
use crate::Term;
7
use crate::storage::THREAD_TERM_POOL;
8

            
9
/// Returns true if the term is an [ATermInt] term.
10
1579909378
pub fn is_int_term<'a, 'b, T: Term<'a, 'b>>(t: &'b T) -> bool {
11
1579909378
    THREAD_TERM_POOL.with(|tp| *tp.int_symbol() == t.get_head_symbol())
12
1579909378
}
13

            
14
/// Returns true if the given symbol is the special integer symbol used by [ATermInt].
15
265438
pub fn is_int_symbol<'a, 'b, S: Symb<'a, 'b>>(f: &'b S) -> bool {
16
265438
    THREAD_TERM_POOL.with(|tp| *tp.int_symbol() == f.copy())
17
265438
}
18

            
19
#[merc_derive_terms]
20
mod inner {
21
    use delegate::delegate;
22

            
23
    use merc_macros::merc_ignore;
24
    use merc_macros::merc_term;
25

            
26
    use crate::ATerm;
27
    use crate::ATermArgs;
28
    use crate::ATermIndex;
29
    use crate::ATermRef;
30
    use crate::Markable;
31
    use crate::SymbolRef;
32
    use crate::Term;
33
    use crate::TermIterator;
34
    use crate::Transmutable;
35
    use crate::is_int_term;
36
    use crate::storage::Marker;
37
    use crate::storage::SharedTermInt;
38
    use crate::storage::THREAD_TERM_POOL;
39

            
40
    /// This is a wrapper around the [ATerm] type that stores a single `u64` using an annotation.
41
    #[merc_term(is_int_term)]
42
    pub struct ATermInt {
43
        term: ATerm,
44
    }
45

            
46
    impl ATermInt {
47
        #[merc_ignore]
48
9250344
        pub fn new(value: usize) -> ATermInt {
49
9250344
            THREAD_TERM_POOL.with(|tp| ATermInt {
50
9250344
                term: tp.create_int(value),
51
9250344
            })
52
9250344
        }
53

            
54
        /// Returns the value of the integer term.
55
        ///
56
        /// # Panics
57
        ///
58
        /// Panics if the term is not an integer term. Use
59
        /// [`ATermInt::value_unchecked`] to skip this check when the term is
60
        /// already known to be an integer term.
61
9253771
        pub fn value(&self) -> usize {
62
9253771
            assert!(is_int_term(self), "value() called on non-integer term {self:?}");
63

            
64
            // SAFETY: We just checked that this is an integer term.
65
9253771
            unsafe { self.value_unchecked() }
66
9253771
        }
67

            
68
        /// Returns the value of the integer term without checking that the
69
        /// term is actually an integer term.
70
        ///
71
        /// # Safety
72
        ///
73
        /// The caller must ensure that the term is an integer term, i.e. that
74
        /// [`is_int_term`] returns true for it. Calling this on a non-integer
75
        /// term reads past the end of the term's allocation, which is
76
        /// undefined behaviour.
77
9253771
        pub unsafe fn value_unchecked(&self) -> usize {
78
            // SAFETY: The caller guarantees that this term wraps an integer term.
79
9253771
            unsafe { self.shared().ptr().cast::<SharedTermInt>().as_ref().value() }
80
9253771
        }
81
    }
82
}
83

            
84
impl fmt::Display for ATermInt {
85
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86
        write!(f, "{}", self.value())
87
    }
88
}
89

            
90
impl fmt::Display for ATermIntRef<'_> {
91
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92
        write!(f, "{}", self.value())
93
    }
94
}
95

            
96
pub use inner::*;
97

            
98
#[cfg(test)]
99
mod tests {
100
    use merc_utilities::test_logger;
101

            
102
    use crate::ATermInt;
103
    use crate::is_int_term;
104

            
105
    #[test]
106
1
    fn test_int_term() {
107
1
        test_logger();
108

            
109
1
        let int_term = ATermInt::new(42);
110
1
        assert_eq!(int_term.value(), 42);
111
1
        assert!(is_int_term(&int_term));
112
1
    }
113
}