1use 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#[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 #[inline]
329 pub const fn zero() -> Self {
330 Self::from_fixnum_wrapping(0)
331 }
332
333 #[inline]
352 pub const fn max_fixnum() -> Self {
353 Self::from_fixnum_wrapping(isize::max_value() >> 1)
354 }
355
356 #[inline]
375 pub const fn min_fixnum() -> Self {
376 Self::from_fixnum_wrapping(isize::min_value() >> 1)
377 }
378
379 #[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 #[inline]
388 pub fn unpack<W: Word>(buf: &[W]) -> Self {
389 Self::unpack_using(buf, Default::default())
390 }
391
392 #[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 #[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 #[inline]
437 pub fn is_positive(self) -> bool {
438 !self.is_negative()
439 }
440
441 #[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 #[inline]
468 pub const fn is_bignum(self) -> bool {
469 !self.is_fixnum()
470 }
471
472 #[inline]
474 pub const fn is_fixnum(self) -> bool {
475 crate::util::value_is_fixnum(self.0.raw())
476 }
477
478 #[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 #[inline]
498 pub const fn to_fixnum_unchecked(self) -> isize {
499 crate::util::value_to_fixnum(self.0.raw())
500 }
501
502 #[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 #[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 #[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 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 #[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 #[inline]
591 pub fn pack<W: Word>(self, buf: &mut [W]) -> pack::Sign {
592 self.pack_using(Default::default(), buf)
593 }
594
595 #[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 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 #[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}