/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.lang; import com.google.gwt.lang.BigLongLibBase.BigLong; /** * Implements a Java <code>long</code> in a way that can be translated to JavaScript. */ public class LongLib { /** * Abstraction for long emulation. The emulation could be done using FastLong if the number is * small enough or BigLong if the number is big. Note that, the class here is just a place holder * and would normally extend JavaScriptObject if there wasn't a JVM mode. */ static class LongEmul { SmallLong small; BigLong big; } /** * A LongEmul represented as double number. */ static class SmallLong { double d; } /** * Allow standalone Java tests such as LongLibTest/LongLibJreTest to run this * code. */ protected static boolean RUN_IN_JVM = false; public static LongEmul add(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) + asDouble(b); if (isSafeIntegerRange(result)) { return createSmallLongEmul(result); } } return createLongEmul(BigLongLib.add(toBigLong(a), toBigLong(b))); } public static LongEmul sub(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) - asDouble(b); if (isSafeIntegerRange(result)) { return createSmallLongEmul(result); } } return createLongEmul(BigLongLib.sub(toBigLong(a), toBigLong(b))); } public static LongEmul neg(LongEmul a) { // TODO: add test for max neg number if (isSmallLong(a)) { double result = 0 - asDouble(a); if (!Double.isNaN(result)) { return createSmallLongEmul(result); } } return createLongEmul(BigLongLib.neg(asBigLong(a))); } public static boolean gt(LongEmul a, LongEmul b) { return compare(a, b) > 0; } public static boolean gte(LongEmul a, LongEmul b) { return compare(a, b) >= 0; } public static boolean lt(LongEmul a, LongEmul b) { return compare(a, b) < 0; } public static boolean lte(LongEmul a, LongEmul b) { return compare(a, b) <= 0; } public static boolean eq(LongEmul a, LongEmul b) { return compare(a, b) == 0; } public static boolean neq(LongEmul a, LongEmul b) { return compare(a, b) != 0; } // VisibleForTesting static double compare(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) - asDouble(b); if (!Double.isNaN(result)) { return result; } } return BigLongLib.compare(toBigLong(a), toBigLong(b)); } public static LongEmul div(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) / asDouble(b); if (isSafeIntegerRange(result)) { return createSmallLongEmul(truncate(result)); } } return createLongEmul(BigLongLib.div(toBigLong(a), toBigLong(b))); } public static LongEmul mod(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) % asDouble(b); if (isSafeIntegerRange(result)) { return createSmallLongEmul(result); } } return createLongEmul(BigLongLib.mod(toBigLong(a), toBigLong(b))); } public static LongEmul mul(LongEmul a, LongEmul b) { if (isSmallLong(a) && isSmallLong(b)) { double result = asDouble(a) * asDouble(b); if (isSafeIntegerRange(result)) { return createSmallLongEmul(result); } } return createLongEmul(BigLongLib.mul(toBigLong(a), toBigLong(b))); } public static LongEmul not(LongEmul a) { return createLongEmul(BigLongLib.not(toBigLong(a))); } public static LongEmul and(LongEmul a, LongEmul b) { return createLongEmul(BigLongLib.and(toBigLong(a), toBigLong(b))); } public static LongEmul or(LongEmul a, LongEmul b) { return createLongEmul(BigLongLib.or(toBigLong(a), toBigLong(b))); } public static LongEmul xor(LongEmul a, LongEmul b) { return createLongEmul(BigLongLib.xor(toBigLong(a), toBigLong(b))); } public static LongEmul shl(LongEmul a, int n) { return createLongEmul(BigLongLib.shl(toBigLong(a), n)); } public static LongEmul shr(LongEmul a, int n) { return createLongEmul(BigLongLib.shr(toBigLong(a), n)); } public static LongEmul shru(LongEmul a, int n) { return createLongEmul(BigLongLib.shru(toBigLong(a), n)); } public static LongEmul fromDouble(double value) { if (isSafeIntegerRange(value)) { return createSmallLongEmul(truncate(value)); } return createLongEmul(BigLongLib.fromDouble(value)); } public static double toDouble(LongEmul a) { if (isSmallLong(a)) { double d = asDouble(a); // We need to kill negative zero because that could never happen in long but our double based // representation may result with that. return d == -0.0 ? 0 : d; } return BigLongLib.toDouble(asBigLong(a)); } public static LongEmul fromInt(int value) { return createSmallLongEmul(value); } public static int toInt(LongEmul a) { if (isSmallLong(a)) { return coerceToInt(asDouble(a)); } return BigLongLib.toInt(asBigLong(a)); } public static String toString(LongEmul a) { if (isSmallLong(a)) { return toString(asDouble(a)); } return BigLongLib.toString(asBigLong(a)); } // Called by compiler to generate constants. public static long[] getAsLongArray(long l) { if (isSafeIntegerRange(l)) { return new long[] {l}; } return BigLongLib.getAsLongArray(l); } // TODO(goktug): Safe integer range could potentially increased update up to 53 bits. private static boolean isSafeIntegerRange(double value) { return -BigLongLibBase.TWO_PWR_44_DBL < value && value < BigLongLibBase.TWO_PWR_44_DBL; } private static double truncate(double value) { // Same as Math.trunc() but not available everywhere. return value < 0 ? Math.ceil(value) : Math.floor(value); } private static int coerceToInt(double value) { if (LongLib.RUN_IN_JVM) { return (int) (long) value; } return coerceToInt0(value); } private static native int coerceToInt0(double value)/*-{ return value | 0; }-*/; private static String toString(double value) { if (LongLib.RUN_IN_JVM) { return String.valueOf((long) value); } return String.valueOf(value); } private static double asDouble(LongEmul value) { return asDouble(asSmallLong(value)); } private static SmallLong asSmallLong(LongEmul value) { if (LongLib.RUN_IN_JVM) { return value.small; } return asSmallLong0(value); } private static native SmallLong asSmallLong0(LongEmul value)/*-{ return value; }-*/; private static double asDouble(SmallLong value) { if (LongLib.RUN_IN_JVM) { return value == null ? Double.NaN : value.d; } return asDouble0(value); } private static native double asDouble0(SmallLong value)/*-{ return value; }-*/; private static boolean isSmallLong(LongEmul value) { if (LongLib.RUN_IN_JVM) { return value.small != null; } return isSmallLong0(value); } private static native boolean isSmallLong0(LongEmul value)/*-{ return typeof(value) === 'number'; }-*/; // Visible for testing static BigLong asBigLong(LongEmul value) { if (LongLib.RUN_IN_JVM) { return value.big; } return asBigLong0(value); } private static native BigLong asBigLong0(LongEmul value)/*-{ return value; }-*/; private static BigLong toBigLong(LongEmul value) { return isSmallLong(value) ? toBigLong(asSmallLong(value)) : asBigLong(value); } private static BigLong toBigLong(SmallLong longValue) { double value = asDouble(longValue); int a3 = 0; if (value < 0) { // Convert to a positive number that will have the exact same first 44 bits value += BigLongLibBase.TWO_PWR_44_DBL; a3 = BigLongLib.MASK_2; } int a1 = (int) (value / BigLongLibBase.TWO_PWR_22_DBL); int a0 = (int) (value - a1 * BigLongLibBase.TWO_PWR_22_DBL); return BigLongLibBase.create(a0, a1, a3); } private static LongEmul createSmallLongEmul(double value) { if (LongLib.RUN_IN_JVM) { SmallLong small = new SmallLong(); small.d = value; LongEmul emul = new LongEmul(); emul.small = small; return emul; } return createSmallLongEmul0(value); } private static native LongEmul createSmallLongEmul0(double value)/*-{ return value; }-*/; private static LongEmul createLongEmul(BigLong big) { int a2 = BigLongLibBase.getH(big); if (a2 == 0) { return createSmallLongEmul( BigLongLibBase.getL(big) + BigLongLibBase.getM(big) * BigLongLibBase.TWO_PWR_22_DBL); } if (a2 == BigLongLibBase.MASK_2) { return createSmallLongEmul(BigLongLibBase.getL(big) + BigLongLibBase.getM(big) * BigLongLibBase.TWO_PWR_22_DBL - BigLongLib.TWO_PWR_44_DBL); } return createBigLongEmul(big); } private static LongEmul createBigLongEmul(BigLong big) { if (LongLib.RUN_IN_JVM) { LongEmul emul = new LongEmul(); emul.big = big; return emul; } return createBigLongEmul0(big); } private static native LongEmul createBigLongEmul0(BigLong value)/*-{ return value; }-*/; // VisibleForTesting static LongEmul copy(LongEmul value) { if (isSmallLong(value)) { return createSmallLongEmul(asDouble(value)); } else { return createBigLongEmul(BigLongLibBase.create(asBigLong(value))); } } /** * Not instantiable. */ private LongLib() { } }