rosy/num/integer/
mod.rs

1//! Ruby integers.
2
3use std::{
4    cmp::Ordering,
5    ffi::c_void,
6    fmt,
7    mem,
8    ops,
9    os::raw::c_int,
10    slice,
11};
12use crate::{
13    prelude::*,
14    object::{NonNullObject, Ty},
15    ruby,
16};
17
18pub mod pack;
19use pack::Word;
20
21/// An instance of Ruby's `Integer` class.
22///
23/// # Logical Binary Operations
24///
25/// The logical operations [AND], [OR], and [XOR] are all supported:
26///
27/// ```
28/// # rosy::vm::init().unwrap();
29/// # rosy::protected(|| {
30/// use rosy::Integer;
31///
32/// let a_val = 0b1101;
33/// let b_val = 0b0111;
34/// let a_int = Integer::from(a_val);
35/// let b_int = Integer::from(b_val);
36///
37/// assert_eq!(a_int & b_int, a_val & b_val);
38/// assert_eq!(a_int | b_int, a_val | b_val);
39/// assert_eq!(a_int ^ b_int, a_val ^ b_val);
40/// # }).unwrap();
41/// ```
42///
43/// [AND]: https://en.wikipedia.org/wiki/Logical_conjunction
44/// [OR]:  https://en.wikipedia.org/wiki/Logical_disjunction
45/// [XOR]: https://en.wikipedia.org/wiki/Exclusive_or
46#[derive(Clone, Copy, Debug)]
47#[repr(transparent)]
48pub struct Integer(NonNullObject);
49
50impl AsRef<AnyObject> for Integer {
51    #[inline]
52    fn as_ref(&self) -> &AnyObject { self.0.as_ref() }
53}
54
55impl From<Integer> for AnyObject {
56    #[inline]
57    fn from(obj: Integer) -> Self { obj.0.into() }
58}
59
60impl<O: Object> PartialEq<O> for Integer {
61    #[inline]
62    fn eq(&self, other: &O) -> bool {
63        let other = other.as_any_object();
64        let other_id = O::unique_id();
65
66        let is_num =
67            other_id == Self::unique_id() ||
68            other_id == Float::unique_id() ||
69            other.is_float() ||
70            other.is_integer();
71
72        if is_num {
73            unsafe { ruby::rb_big_eq(self.raw(), other.raw()) != 0 }
74        } else {
75            self.as_any_object() == other
76        }
77    }
78}
79
80impl Eq for Integer {}
81
82impl PartialOrd for Integer {
83    #[inline]
84    fn partial_cmp(&self, other: &Integer) -> Option<Ordering> {
85        Some(self.cmp(other))
86    }
87}
88
89impl Ord for Integer {
90    #[inline]
91    fn cmp(&self, other: &Integer) -> Ordering {
92        let raw = unsafe { ruby::rb_big_cmp(self.raw(), other.raw()) };
93        crate::util::value_to_fixnum(raw).cmp(&0)
94    }
95}
96
97unsafe impl Object for Integer {
98    #[inline]
99    fn unique_id() -> Option<u128> {
100        Some(!((Ty::FIXNUM.id() as u128) | ((Ty::BIGNUM.id() as u128) << 8)))
101    }
102
103    #[inline]
104    fn cast<A: Object>(object: A) -> Option<Self> {
105        if object.into_any_object().is_integer() {
106            unsafe { Some(Self::cast_unchecked(object)) }
107        } else {
108            None
109        }
110    }
111
112    #[inline]
113    fn ty(self) -> Ty {
114        if self.is_fixnum() {
115            Ty::FIXNUM
116        } else {
117            Ty::BIGNUM
118        }
119    }
120
121    #[inline]
122    fn is_ty(self, ty: Ty) -> bool {
123        self.ty() == ty
124    }
125}
126
127impl From<usize> for Integer {
128    #[inline]
129    fn from(int: usize) -> Self {
130        unsafe { Self::from_raw(ruby::rb_uint2inum(int)) }
131    }
132}
133
134impl From<isize> for Integer {
135    #[inline]
136    fn from(int: isize) -> Self {
137        unsafe { Self::from_raw(ruby::rb_int2inum(int)) }
138    }
139}
140
141impl From<u128> for Integer {
142    #[inline]
143    fn from(int: u128) -> Self {
144        Self::unpack(slice::from_ref(&int))
145    }
146}
147
148impl From<i128> for Integer {
149    #[inline]
150    fn from(int: i128) -> Self {
151        Self::unpack(slice::from_ref(&int))
152    }
153}
154
155impl From<u64> for Integer {
156    #[inline]
157    fn from(int: u64) -> Self {
158        if mem::size_of::<u64>() == mem::size_of::<usize>() {
159            (int as usize).into()
160        } else {
161            Self::unpack(slice::from_ref(&int))
162        }
163    }
164}
165
166impl From<i64> for Integer {
167    #[inline]
168    fn from(int: i64) -> Self {
169        if mem::size_of::<i64>() == mem::size_of::<isize>() {
170            (int as isize).into()
171        } else {
172            Self::unpack(slice::from_ref(&int))
173        }
174    }
175}
176
177impl From<u32> for Integer {
178    #[inline]
179    fn from(int: u32) -> Self {
180        (int as usize).into()
181    }
182}
183
184impl From<i32> for Integer {
185    #[inline]
186    fn from(int: i32) -> Self {
187        (int as isize).into()
188    }
189}
190
191impl From<u16> for Integer {
192    #[inline]
193    fn from(int: u16) -> Self {
194        (int as usize).into()
195    }
196}
197
198impl From<i16> for Integer {
199    #[inline]
200    fn from(int: i16) -> Self {
201        (int as isize).into()
202    }
203}
204
205impl From<u8> for Integer {
206    #[inline]
207    fn from(int: u8) -> Self {
208        (int as usize).into()
209    }
210}
211
212impl From<i8> for Integer {
213    #[inline]
214    fn from(int: i8) -> Self {
215        (int as isize).into()
216    }
217}
218
219macro_rules! forward_from {
220    ($($t:ty)+) => { $(
221        impl From<$t> for AnyObject {
222            #[inline]
223            fn from(int: $t) -> Self {
224                Integer::from(int).into()
225            }
226        }
227    )+ }
228}
229
230forward_from! {
231    usize u128 u64 u32 u16 u8
232    isize i128 i64 i32 i16 i8
233}
234
235macro_rules! forward_cmp {
236    ($($t:ty)+) => { $(
237        impl PartialEq<$t> for Integer {
238            #[inline]
239            fn eq(&self, other: &$t) -> bool {
240                if let Some(this) = self.to_value::<$t>() {
241                    this == *other
242                } else {
243                    false
244                }
245            }
246        }
247
248        impl PartialEq<Integer> for $t {
249            #[inline]
250            fn eq(&self, other: &Integer) -> bool {
251                other == self
252            }
253        }
254
255        impl PartialOrd<$t> for Integer {
256            #[inline]
257            fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
258                let (can_represent, is_negative) = self._can_represent::<$t>();
259
260                if can_represent {
261                    let mut this: $t = 0;
262                    let sign = self.pack(slice::from_mut(&mut this));
263                    debug_assert!(!sign.did_overflow(), "Overflow on {}", self);
264
265                    Some(this.cmp(other))
266                } else if is_negative {
267                    Some(Ordering::Less)
268                } else {
269                    Some(Ordering::Greater)
270                }
271            }
272        }
273
274        impl PartialOrd<Integer> for $t {
275            #[inline]
276            fn partial_cmp(&self, other: &Integer) -> Option<Ordering> {
277                Some(other.partial_cmp(self)?.reverse())
278            }
279        }
280    )+ }
281}
282
283forward_cmp! {
284    usize u128 u64 u32 u16 u8
285    isize i128 i64 i32 i16 i8
286}
287
288macro_rules! impl_bit_ops {
289    ($($op:ident, $f:ident, $r:ident;)+) => { $(
290        impl ops::$op for Integer {
291            type Output = Self;
292
293            #[inline]
294            fn $f(self, rhs: Self) -> Self {
295                let (a, b) = match (self.to_fixnum(), rhs.to_fixnum()) {
296                    (Some(a), Some(b)) => {
297                        return Self::from_fixnum_wrapping(a.$f(b));
298                    },
299                    (Some(_), None) => (rhs, self),
300                    (None,    _)    => (self, rhs),
301                };
302                unsafe { Self::from_raw(ruby::$r(a.raw(), b.raw())) }
303            }
304        }
305    )+ }
306}
307
308impl_bit_ops! {
309    BitAnd, bitand, rb_big_and;
310    BitOr,  bitor,  rb_big_or;
311    BitXor, bitxor, rb_big_xor;
312}
313
314impl fmt::Display for Integer {
315    #[inline]
316    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317        self.as_any_object().fmt(f)
318    }
319}
320
321impl Integer {
322    #[inline]
323    const unsafe fn _from_raw(raw: ruby::VALUE) -> Self {
324        Self(NonNullObject::from_raw(raw))
325    }
326
327    /// Returns an instance with a value of 0.
328    #[inline]
329    pub const fn zero() -> Self {
330        Self::from_fixnum_wrapping(0)
331    }
332
333    /// Returns the maximum value that may be used as a fixnum.
334    ///
335    /// # Examples
336    ///
337    /// This value is equal to
338    /// [`isize::max_value()`](https://doc.rust-lang.org/std/primitive.isize.html#method.max_value)
339    /// shifted right by 1.
340    ///
341    /// ```
342    /// # rosy::vm::init().unwrap();
343    /// use rosy::Integer;
344    ///
345    /// let fixnum = isize::max_value() >> 1;
346    /// let integer = Integer::from(fixnum);
347    ///
348    /// assert_eq!(integer, Integer::max_fixnum());
349    /// assert_eq!(integer.to_fixnum(), Some(fixnum));
350    /// ```
351    #[inline]
352    pub const fn max_fixnum() -> Self {
353        Self::from_fixnum_wrapping(isize::max_value() >> 1)
354    }
355
356    /// Returns the minimum value that may be used as a fixnum.
357    ///
358    /// # Examples
359    ///
360    /// This value is equal to
361    /// [`isize::min_value()`](https://doc.rust-lang.org/std/primitive.isize.html#method.min_value)
362    /// shifted right by 1.
363    ///
364    /// ```
365    /// # rosy::vm::init().unwrap();
366    /// use rosy::Integer;
367    ///
368    /// let fixnum = isize::min_value() >> 1;
369    /// let integer = Integer::from(fixnum);
370    ///
371    /// assert_eq!(integer, Integer::min_fixnum());
372    /// assert_eq!(integer.to_fixnum(), Some(fixnum));
373    /// ```
374    #[inline]
375    pub const fn min_fixnum() -> Self {
376        Self::from_fixnum_wrapping(isize::min_value() >> 1)
377    }
378
379    /// Returns an instance from the fixed-size number, wrapping at the most
380    /// significant bit.
381    #[inline]
382    pub const fn from_fixnum_wrapping(n: isize) -> Self {
383        unsafe { Self::_from_raw(crate::util::fixnum_to_value(n)) }
384    }
385
386    /// Unpacks the contents of `buf` into a new instance.
387    #[inline]
388    pub fn unpack<W: Word>(buf: &[W]) -> Self {
389        Self::unpack_using(buf, Default::default())
390    }
391
392    /// Unpacks the contents of `buf` into a new instance using `options`.
393    #[inline]
394    pub fn unpack_using<W: Word>(buf: &[W], options: pack::Options) -> Self {
395        use ruby::integer_flags::*;
396
397        let ptr = buf.as_ptr() as *const c_void;
398        let len = buf.len();
399        let size = mem::size_of::<W>();
400
401        let two = (W::IS_SIGNED as c_int) * PACK_2COMP;
402        let neg = (options.is_negative as c_int) * PACK_NEGATIVE;
403        let flags = options.flags() | two | neg;
404
405        unsafe {
406            Self::from_raw(ruby::rb_integer_unpack(ptr, len, size, 0, flags))
407        }
408    }
409
410    /// Returns whether `self == 0`.
411    #[inline]
412    pub fn is_zero(self) -> bool {
413        if let Some(fixnum) = self.to_fixnum() {
414            fixnum == 0
415        } else {
416            unsafe { ruby::rb_bigzero_p(self.raw()) != 0 }
417        }
418    }
419
420    /// Returns whether `self >= 0`.
421    ///
422    /// # Examples
423    ///
424    /// ```
425    /// # rosy::vm::init().unwrap();
426    /// use rosy::Integer;
427    ///
428    /// let big = Integer::from(u128::max_value());
429    /// let fix = Integer::from(isize::max_value() / 2);
430    /// # assert!(big.is_bignum());
431    /// # assert!(fix.is_fixnum());
432    ///
433    /// assert!(big.is_positive());
434    /// assert!(fix.is_positive());
435    /// ```
436    #[inline]
437    pub fn is_positive(self) -> bool {
438        !self.is_negative()
439    }
440
441    /// Returns whether `self < 0`.
442    ///
443    /// # Examples
444    ///
445    /// ```
446    /// # rosy::vm::init().unwrap();
447    /// use rosy::Integer;
448    ///
449    /// let big = Integer::from(i128::min_value());
450    /// let fix = Integer::from(isize::min_value() / 2);
451    /// # assert!(big.is_bignum());
452    /// # assert!(fix.is_fixnum());
453    ///
454    /// assert!(big.is_negative());
455    /// assert!(fix.is_negative());
456    /// ```
457    #[inline]
458    pub fn is_negative(self) -> bool {
459        if let Some(fixnum) = self.to_fixnum() {
460            fixnum < 0
461        } else {
462            unsafe { ruby::rb_big_sign(self.raw()) == 0 }
463        }
464    }
465
466    /// Returns whether `self` is a variable-width integer.
467    #[inline]
468    pub const fn is_bignum(self) -> bool {
469        !self.is_fixnum()
470    }
471
472    /// Returns whether `self` is a fixed-width integer.
473    #[inline]
474    pub const fn is_fixnum(self) -> bool {
475        crate::util::value_is_fixnum(self.0.raw())
476    }
477
478    /// Returns the value of the fixed-width integer stored in `self`, if it is
479    /// not a bignum.
480    #[inline]
481    pub fn to_fixnum(self) -> Option<isize> {
482        if self.is_fixnum() {
483            Some(crate::util::value_to_fixnum(self.raw()))
484        } else {
485            None
486        }
487    }
488
489    /// Returns the value of the fixed-width integer stored in `self`, assuming
490    /// it is not a bignum.
491    ///
492    /// # Safety
493    ///
494    /// This method is not marked as `unsafe` because using it on a bignum is
495    /// simply a programming error and will not result in memory or type
496    /// unsafety.
497    #[inline]
498    pub const fn to_fixnum_unchecked(self) -> isize {
499        crate::util::value_to_fixnum(self.0.raw())
500    }
501
502    /// Converts `self` to `W` if it can be represented as `W`.
503    #[inline]
504    pub fn to_value<W: Word>(self) -> Option<W> {
505        if !self.can_represent::<W>() {
506            return None;
507        }
508        let mut val = W::ZERO;
509        let sign = self.pack(slice::from_mut(&mut val));
510        debug_assert!(!sign.did_overflow());
511        Some(val)
512    }
513
514    /// Converts `self` to its inner value as `W`, truncating on too large or
515    /// small of a value.
516    ///
517    /// # Examples
518    ///
519    /// This has the same exact behavior as an
520    /// [`as` cast](https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions)
521    /// between integer primitives in Rust:
522    ///
523    /// ```
524    /// # rosy::vm::init().unwrap();
525    /// # rosy::protected(|| {
526    /// let val = u16::max_value();
527    /// let int = rosy::Integer::from(val);
528    ///
529    /// assert_eq!(int.to_truncated::<u16>(), val);
530    /// assert_eq!(int.to_truncated::<u8>(),  255);
531    /// assert_eq!(int.to_truncated::<i8>(),   -1);
532    /// # }).unwrap();
533    /// ```
534    #[inline]
535    pub fn to_truncated<W: Word>(self) -> W {
536        let mut val = W::ZERO;
537        self.pack(slice::from_mut(&mut val));
538        val
539    }
540
541    /// Returns `self` as a 64-bit floating point number.
542    ///
543    /// Note that this is very likely to be a lossy conversion.
544    #[inline]
545    pub fn to_f64(self) -> f64 {
546        if let Some(fixnum) = self.to_fixnum() {
547            fixnum as f64
548        } else {
549            unsafe { ruby::rb_big2dbl(self.raw()) }
550        }
551    }
552
553    /// Returns a string for `self` in the given base, or an exception if one is
554    /// raised.
555    pub fn to_s_radix(self, radix: u32) -> Result<String> {
556        unsafe {
557            crate::protected_no_panic(|| self.to_s_radix_unchecked(radix))
558        }
559    }
560
561    /// Returns a string for `self` in the given base.
562    ///
563    /// # Safety
564    ///
565    /// An exception will be raised if `self` is too large or if `radix > 36`.
566    #[inline]
567    pub unsafe fn to_s_radix_unchecked(self, radix: u32) -> String {
568        String::from_raw(ruby::rb_big2str(self.raw(), radix as _))
569    }
570
571    /// Packs the contents of `self` into `buf` with the platform's native byte
572    /// order.
573    ///
574    /// # Examples
575    ///
576    /// ```
577    /// # rosy::vm::init().unwrap();
578    /// # rosy::protected(|| {
579    /// use std::slice;
580    /// use rosy::Integer;
581    ///
582    /// let value = u128::max_value() / 0xF00F;
583    /// let integer = Integer::from(value);
584    ///
585    /// let mut buf = [0u128; 2];
586    /// integer.pack(&mut buf);
587    /// assert_eq!(buf[0], value);
588    /// # }).unwrap();
589    /// ```
590    #[inline]
591    pub fn pack<W: Word>(self, buf: &mut [W]) -> pack::Sign {
592        self.pack_using(Default::default(), buf)
593    }
594
595    /// Packs the contents of `self` into `buf` using `options`.
596    ///
597    /// # Examples
598    ///
599    /// ```
600    /// # rosy::vm::init().unwrap();
601    /// # rosy::protected(|| {
602    /// use std::slice;
603    /// use rosy::num::{Integer, pack::Options};
604    ///
605    /// let value = u128::max_value() / 0xF00F;
606    /// let integer = Integer::from(value);
607    ///
608    /// let mut be_buf = [0u128; 1];
609    /// integer.pack_using(Options::big_endian(), &mut be_buf);
610    /// assert_eq!(be_buf[0], value.to_be());
611    ///
612    /// let mut le_buf = [0u128; 1];
613    /// integer.pack_using(Options::little_endian(), &mut le_buf);
614    /// assert_eq!(le_buf[0], value.to_le());
615    /// # }).unwrap();
616    /// ```
617    #[inline]
618    pub fn pack_using<W: Word>(
619        self,
620        options: pack::Options,
621        buf: &mut [W],
622    ) -> pack::Sign {
623        use ruby::integer_flags::*;
624        use pack::Sign::*;
625
626        let raw = self.raw();
627        let ptr = buf.as_mut_ptr() as *mut c_void;
628        let num = buf.len();
629        let size = mem::size_of::<W>();
630
631        let flags = options.flags() | ((W::IS_SIGNED as c_int) * PACK_2COMP);
632
633        match unsafe { ruby::rb_integer_pack(raw, ptr, num, size, 0, flags) } {
634            02 => Positive { did_overflow: true },
635            01 => Positive { did_overflow: false },
636            00 => Zero,
637            -1 => Negative { did_overflow: false },
638            _  => Negative { did_overflow: true },
639        }
640    }
641
642    fn _can_represent_raw(self, signed: bool, word_size: usize) -> (bool, bool) {
643        // Taken from documentation of `rb_absint_singlebit_p`
644        let is_negative = self.is_negative();
645        let raw = self.raw();
646
647        let mut nlz_bits = 0;
648        let mut size = unsafe { ruby::rb_absint_size(raw, &mut nlz_bits) };
649
650        let can_represent = if signed {
651            let single_bit = unsafe { ruby::rb_absint_singlebit_p(raw) != 0 };
652            if nlz_bits == 0 && !(is_negative && single_bit) {
653                size += 1
654            }
655            size <= word_size
656        } else if is_negative {
657            false
658        } else {
659            size <= word_size
660        };
661        (can_represent, is_negative)
662    }
663
664    #[inline]
665    fn _can_represent<W: Word>(self) -> (bool, bool) {
666        self._can_represent_raw(W::IS_SIGNED, mem::size_of::<W>())
667    }
668
669    /// Returns whether `self` can represent the word type `W`.
670    #[inline]
671    pub fn can_represent<W: Word>(self) -> bool {
672        self._can_represent::<W>().0
673    }
674}
675
676#[cfg(test)]
677mod tests {
678    use super::*;
679
680    #[test]
681    fn values() {
682        crate::vm::init().unwrap();
683
684        macro_rules! test {
685            ($($t:ty)+) => { $({
686                let values = [
687                    0,
688                    <$t>::min_value(),
689                    <$t>::max_value(),
690                ];
691                for &value in &values {
692                    let int = Integer::from(value);
693                    assert_eq!(int.to_s(), value.to_string());
694
695                    let converted = int.to_value::<$t>()
696                        .expect(&format!("{} cannot represent {}", int, value));
697                    assert_eq!(converted, value);
698
699                    let mut buf: [$t; 1] = [0];
700                    let sign = int.pack(&mut buf);
701                    assert!(
702                        !sign.did_overflow(),
703                        "Packing {} from {} overflowed as {:?}",
704                        int,
705                        value,
706                        sign,
707                    );
708                    assert_eq!(buf[0], value);
709                }
710            })+ }
711        }
712
713        crate::protected(|| {
714            test! {
715                usize u128 u64 u32 u16 u8
716                isize i128 i64 i32 i16 i8
717            }
718        }).unwrap();
719    }
720
721    #[test]
722    fn bit_ops() {
723        crate::vm::init().unwrap();
724
725        macro_rules! test {
726            ($($t:ty)+) => { $({
727                let min = <$t>::min_value();
728                let max = <$t>::max_value();
729
730                let min_int = Integer::from(min);
731                let max_int = Integer::from(max);
732
733                assert_eq!(min_int & min_int, min & min);
734                assert_eq!(min_int & max_int, min & max);
735                assert_eq!(max_int & min_int, max & min);
736                assert_eq!(max_int & max_int, max & max);
737
738                assert_eq!(min_int | min_int, min | min);
739                assert_eq!(min_int | max_int, min | max);
740                assert_eq!(max_int | min_int, max | min);
741                assert_eq!(max_int | max_int, max | max);
742
743                assert_eq!(min_int ^ min_int, min ^ min);
744                assert_eq!(min_int ^ max_int, min ^ max);
745                assert_eq!(max_int ^ min_int, max ^ min);
746                assert_eq!(max_int ^ max_int, max ^ max);
747            })+ };
748        }
749
750        crate::protected(|| {
751            test! {
752                usize u128 u64 u32 u16 u8
753                isize i128 i64 i32 i16 i8
754            }
755        }).unwrap();
756    }
757}