ObjectIDOperations.java

  1. /*
  2.  * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
  3.  * code is released under a tri EPL/GPL/LGPL license. You can use it,
  4.  * redistribute it and/or modify it under the terms of the:
  5.  *
  6.  * Eclipse Public License version 1.0
  7.  * GNU General Public License version 2
  8.  * GNU Lesser General Public License version 2.1
  9.  */
  10. package org.jruby.truffle.runtime;

  11. import com.oracle.truffle.api.ExactMath;
  12. import org.jruby.truffle.runtime.core.RubyBignum;

  13. import java.math.BigInteger;

  14. /**
  15.  * <pre>
  16.  * Object IDs distribution
  17.  *
  18.  * We try to respect MRI scheme when it makes sense (Fixnum for the moment).
  19.  * Have a look at include/ruby/ruby.h below ruby_special_consts.
  20.  *
  21.  * Encoding for Fixnum (long):
  22.  * ... 0000 = false
  23.  * ... 0010 = true
  24.  * ... 0100 = nil
  25.  *
  26.  * ... xxx1 = Fixnum of value (id-1)/2 if -2^62 <= value < 2^62
  27.  * ... xxx0 = BasicObject generated id (for id > 4)
  28.  *
  29.  * Encoding for Bignum:
  30.  * ... 0001 | 64-bit long = Fixnum if value < -2^62 or value >= 2^62
  31.  * ... 0010 | 64-bit raw double bits = Float
  32.  * </pre>
  33.  */
  34. public abstract class ObjectIDOperations {

  35.     public static final int FALSE = 0;
  36.     public static final int TRUE = 2;
  37.     public static final int NIL = 4;
  38.     public static final int FIRST_OBJECT_ID = 6;

  39.     private static final BigInteger LARGE_FIXNUM_FLAG = BigInteger.ONE.shiftLeft(64);
  40.     private static final BigInteger FLOAT_FLAG = BigInteger.ONE.shiftLeft(65);

  41.     private static final long SMALL_FIXNUM_MIN = -(1L << 62);
  42.     private static final long SMALL_FIXNUM_MAX = (1L << 62) - 1;

  43.     // primitive => ID

  44.     public static boolean isSmallFixnum(long fixnum) {
  45.         // TODO: optimize
  46.         return SMALL_FIXNUM_MIN <= fixnum && fixnum <= SMALL_FIXNUM_MAX;
  47.     }

  48.     public static long smallFixnumToIDOverflow(long fixnum) throws ArithmeticException{
  49.         return ExactMath.addExact(ExactMath.multiplyExact(fixnum, 2), 1);
  50.     }

  51.     public static long smallFixnumToID(long fixnum) {
  52.         assert isSmallFixnum(fixnum);
  53.         return fixnum * 2 + 1;
  54.     }

  55.     public static RubyBignum largeFixnumToID(RubyContext context, long fixnum) {
  56.         assert !isSmallFixnum(fixnum);
  57.         return new RubyBignum(context.getCoreLibrary().getBignumClass(), BigInteger.valueOf(fixnum).or(LARGE_FIXNUM_FLAG));
  58.     }

  59.     public static RubyBignum floatToID(RubyContext context, double value) {
  60.         long bits = Double.doubleToRawLongBits(value);
  61.         return new RubyBignum(context.getCoreLibrary().getBignumClass(), BigInteger.valueOf(bits).or(FLOAT_FLAG));
  62.     }

  63.     // ID => primitive

  64.     public static boolean isSmallFixnumID(long id) {
  65.         return id % 2 != 0;
  66.     }

  67.     public static long toFixnum(long id) {
  68.         return (id - 1) / 2;
  69.     }

  70.     public static boolean isLargeFixnumID(RubyBignum id) {
  71.         return !id.bigIntegerValue().and(LARGE_FIXNUM_FLAG).equals(BigInteger.ZERO);
  72.     }

  73.     public static long toFixnum(RubyBignum id) {
  74.         return id.longValue();
  75.     }

  76.     public static boolean isFloatID(RubyBignum id) {
  77.         return !id.bigIntegerValue().and(FLOAT_FLAG).equals(BigInteger.ZERO);
  78.     }

  79.     public static double toFloat(RubyBignum id) {
  80.         return Double.longBitsToDouble(id.longValue());
  81.     }

  82. }