otter/types/terms/
integer.rs1use core::marker::PhantomData;
6
7#[cfg(feature = "bigint")]
8use num_bigint::{BigInt, Sign};
9
10use crate::types::sealed::Sealed;
11use crate::types::{Env, Invariant, RawTerm, Term};
12
13#[derive(Clone, Copy)]
20pub struct Integer<'id> {
21 raw_term: RawTerm,
22 _id: Invariant<'id>,
23}
24
25impl<'id> Integer<'id> {
26 #[crate::raw]
27 pub(crate) fn from_raw(raw_term: RawTerm) -> Self {
28 Self { raw_term, _id: PhantomData }
29 }
30
31 pub fn from_i64(env: impl Env<'id>, val: i64) -> Self {
33 let raw_term = unsafe { enif_ffi::make_int64(env.raw_env(), val) };
34 Integer { raw_term, _id: PhantomData }
35 }
36
37 pub fn from_u64(env: impl Env<'id>, val: u64) -> Self {
39 let raw_term = unsafe { enif_ffi::make_uint64(env.raw_env(), val) };
40 Integer { raw_term, _id: PhantomData }
41 }
42
43 pub fn to_i64(self, env: impl Env<'id>) -> Option<i64> {
46 let mut val: i64 = 0;
47 (unsafe { enif_ffi::get_int64(env.raw_env(), self.raw_term, &mut val) } != 0).then_some(val)
48 }
49
50 pub fn to_u64(self, env: impl Env<'id>) -> Option<u64> {
53 let mut val: u64 = 0;
54 (unsafe { enif_ffi::get_uint64(env.raw_env(), self.raw_term, &mut val) } != 0).then_some(val)
55 }
56
57 #[cfg(feature = "bigint")]
69 #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
70 pub fn to_bigint(self, env: impl Env<'id>) -> BigInt {
71 let buf = crate::types::serialize(env, self)
72 .expect("enif_term_to_binary failed on an integer term");
73 etf_integer_to_bigint(buf.as_bytes())
74 }
75
76 #[cfg(feature = "bigint")]
86 #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
87 pub fn from_bigint(env: impl Env<'id>, val: &BigInt) -> Self {
88 if let Ok(i) = i64::try_from(val) {
89 return Self::from_i64(env, i);
90 }
91 if let Ok(u) = u64::try_from(val) {
92 return Self::from_u64(env, u);
93 }
94 let etf = bigint_to_etf(val);
95 let term = crate::types::deserialize(env, &etf, false)
96 .expect("enif_binary_to_term failed on otter-authored bignum ETF");
97 Integer::from_raw(term.raw_term())
98 }
99}
100
101#[cfg(feature = "bigint")]
104const ETF_VERSION: u8 = 131;
105#[cfg(feature = "bigint")]
106const SMALL_INTEGER_EXT: u8 = 97; #[cfg(feature = "bigint")]
108const INTEGER_EXT: u8 = 98; #[cfg(feature = "bigint")]
110const SMALL_BIG_EXT: u8 = 110; #[cfg(feature = "bigint")]
112const LARGE_BIG_EXT: u8 = 111; #[cfg(feature = "bigint")]
118fn etf_integer_to_bigint(bytes: &[u8]) -> BigInt {
119 assert!(
120 bytes.len() >= 2 && bytes[0] == ETF_VERSION,
121 "malformed ETF from enif_term_to_binary"
122 );
123 let body = &bytes[1..];
124 match body[0] {
125 SMALL_INTEGER_EXT => BigInt::from(body[1]),
126 INTEGER_EXT => BigInt::from(i32::from_be_bytes([body[1], body[2], body[3], body[4]])),
127 SMALL_BIG_EXT => {
128 let n = body[1] as usize;
129 BigInt::from_bytes_le(etf_sign(body[2]), &body[3..3 + n])
130 }
131 LARGE_BIG_EXT => {
132 let n = u32::from_be_bytes([body[1], body[2], body[3], body[4]]) as usize;
133 BigInt::from_bytes_le(etf_sign(body[5]), &body[6..6 + n])
134 }
135 tag => panic!("enif_term_to_binary of an integer produced unexpected ETF tag {tag}"),
136 }
137}
138
139#[cfg(feature = "bigint")]
141fn etf_sign(byte: u8) -> Sign {
142 if byte == 0 { Sign::Plus } else { Sign::Minus }
143}
144
145#[cfg(feature = "bigint")]
148fn bigint_to_etf(val: &BigInt) -> Vec<u8> {
149 let (sign, mag) = val.to_bytes_le();
150 let sign_byte = if sign == Sign::Minus { 1 } else { 0 };
151 let mut etf = Vec::with_capacity(mag.len() + 7);
152 etf.push(ETF_VERSION);
153 if mag.len() <= u8::MAX as usize {
154 etf.push(SMALL_BIG_EXT);
155 etf.push(mag.len() as u8);
156 etf.push(sign_byte);
157 } else {
158 etf.push(LARGE_BIG_EXT);
159 etf.extend_from_slice(&(mag.len() as u32).to_be_bytes());
160 etf.push(sign_byte);
161 }
162 etf.extend_from_slice(&mag);
163 etf
164}
165
166impl<'id> Sealed for Integer<'id> {}
167
168impl<'id> Term<'id> for Integer<'id> {
169 fn raw_term(self) -> RawTerm {
170 self.raw_term
171 }
172}
173
174impl PartialEq for Integer<'_> {
175 fn eq(&self, other: &Self) -> bool {
176 unsafe { enif_ffi::is_identical(self.raw_term, other.raw_term) != 0 }
177 }
178}
179
180impl Eq for Integer<'_> {}
181
182impl PartialOrd for Integer<'_> {
183 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
184 Some(self.cmp(other))
185 }
186}
187
188impl Ord for Integer<'_> {
189 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
190 let c = unsafe { enif_ffi::compare(self.raw_term, other.raw_term) };
191 c.cmp(&0)
192 }
193}
194
195impl std::fmt::Debug for Integer<'_> {
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 write!(f, "Integer")
198 }
199}