/* * %CopyrightBegin% * * Copyright Ericsson AB 2000-2009. All Rights Reserved. * * 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. * * %CopyrightEnd% */ package com.ericsson.otp.erlang; import java.math.BigInteger; /** * Provides a Java representation of Erlang integral types. Erlang does not distinguish * between different integral types, however this class and its subclasses * {@link OtpErlangByte}, {@link OtpErlangChar}, {@link OtpErlangInt} , and * {@link OtpErlangShort} attempt to map the Erlang types onto the various Java integral * types. Two additional classes, {@link OtpErlangUInt} and {@link OtpErlangUShort} are * provided for Corba compatibility. See the documentation for IC for more information. */ public class OtpErlangLong extends OtpErlangObject { // don't change this! private static final long serialVersionUID = 1610466859236755096L; private long val; private BigInteger bigVal = null; /** * Create an Erlang integer from the given value. * * @param l * the long value to use. */ public OtpErlangLong(final long l) { val = l; } /** * Create an Erlang integer from the given value. * * @param v * the big integer value to use. */ public OtpErlangLong(final BigInteger v) { if (v == null) { throw new java.lang.NullPointerException(); } if (v.bitLength() < 64) { val = v.longValue(); } else { bigVal = v; } } /** * Create an Erlang integer from a stream containing an integer encoded in Erlang * external format. * * @param buf * the stream containing the encoded value. * * @exception OtpErlangDecodeException * if the buffer does not contain a valid external representation of an * Erlang integer. */ public OtpErlangLong(final OtpInputStream buf) throws OtpErlangDecodeException { final byte[] b = buf.read_integer_byte_array(); try { val = OtpInputStream.byte_array_to_long(b, false); } catch (final OtpErlangDecodeException e) { bigVal = new BigInteger(b); } } /** * Get this number as a BigInteger. * * @return the value of this number, as a BigInteger. */ public BigInteger bigIntegerValue() { if (bigVal != null) { return bigVal; } return BigInteger.valueOf(val); } /** * Get this number as a long, or rather truncate all but the least significant 64 bits * from the 2's complement representation of this number and return them as a long. * * @return the value of this number, as a long. */ public long longValue() { if (bigVal != null) { return bigVal.longValue(); } return val; } /** * Determine if this value can be represented as a long without truncation. * * @return true if this value fits in a long, false otherwise. */ public boolean isLong() { // To just chech this.bigVal is a wee bit to simple, since // there just might have be a mean bignum that arrived on // a stream, and was a long disguised as more than 8 byte integer. if (bigVal != null) { return bigVal.bitLength() < 64; } return true; } /** * Determine if this value can be represented as an unsigned long without truncation, * that is if the value is non-negative and its bit pattern completely fits in a long. * * @return true if this value is non-negative and fits in a long false otherwise. */ public boolean isULong() { // Here we have the same problem as for isLong(), plus // the whole range 1<<63 .. (1<<64-1) is allowed. if (bigVal != null) { return bigVal.signum() >= 0 && bigVal.bitLength() <= 64; } return val >= 0; } /** * Returns the number of bits in the minimal two's-complement representation of this * BigInteger, excluding a sign bit. * * @return number of bits in the minimal two's-complement representation of this * BigInteger, excluding a sign bit. */ public int bitLength() { if (bigVal != null) { return bigVal.bitLength(); } if (val == 0 || val == -1) { return 0; } // Binary search for bit length int i = 32; // mask length long m = (1L << i) - 1; // AND mask with ones in little end if (val < 0) { m = ~m; // OR mask with ones in big end for (int j = i >> 1; j > 0; j >>= 1) { // mask delta if ((val | m) == val) { // mask >= enough i -= j; m >>= j; // try less bits } else { i += j; m <<= j; // try more bits } } if ((val | m) != val) { i++; // mask < enough } } else { for (int j = i >> 1; j > 0; j >>= 1) { // mask delta if ((val & m) == val) { // mask >= enough i -= j; m >>= j; // try less bits } else { i += j; m = m << j | m; // try more bits } } if ((val & m) != val) { i++; // mask < enough } } return i; } /** * Return the signum function of this object. * * @return -1, 0 or 1 as the value is negative, zero or positive. */ public int signum() { if (bigVal != null) { return bigVal.signum(); } return val > 0 ? 1 : val < 0 ? -1 : 0; } /** * Get this number as an int. * * @return the value of this number, as an int. * * @exception OtpErlangRangeException * if the value is too large to be represented as an int. */ public int intValue() throws OtpErlangRangeException { final long l = longValue(); final int i = (int) l; if (i != l) { throw new OtpErlangRangeException("Value too large for int: " + val); } return i; } /** * Get this number as a non-negative int. * * @return the value of this number, as an int. * * @exception OtpErlangRangeException * if the value is too large to be represented as an int, or if the * value is negative. */ public int uIntValue() throws OtpErlangRangeException { final long l = longValue(); final int i = (int) l; if (i != l) { throw new OtpErlangRangeException("Value too large for int: " + val); } else if (i < 0) { throw new OtpErlangRangeException("Value not positive: " + val); } return i; } /** * Get this number as a short. * * @return the value of this number, as a short. * * @exception OtpErlangRangeException * if the value is too large to be represented as a short. */ public short shortValue() throws OtpErlangRangeException { final long l = longValue(); final short i = (short) l; if (i != l) { throw new OtpErlangRangeException("Value too large for short: " + val); } return i; } /** * Get this number as a non-negative short. * * @return the value of this number, as a short. * * @exception OtpErlangRangeException * if the value is too large to be represented as a short, or if the * value is negative. */ public short uShortValue() throws OtpErlangRangeException { final long l = longValue(); final short i = (short) l; if (i != l) { throw new OtpErlangRangeException("Value too large for short: " + val); } else if (i < 0) { throw new OtpErlangRangeException("Value not positive: " + val); } return i; } /** * Get this number as a char. * * @return the char value of this number. * * @exception OtpErlangRangeException * if the value is too large to be represented as a char. */ public char charValue() throws OtpErlangRangeException { final long l = longValue(); final char i = (char) l; if (i != l) { throw new OtpErlangRangeException("Value too large for char: " + val); } return i; } /** * Get this number as a byte. * * @return the byte value of this number. * * @exception OtpErlangRangeException * if the value is too large to be represented as a byte. */ public byte byteValue() throws OtpErlangRangeException { final long l = longValue(); final byte i = (byte) l; if (i != l) { throw new OtpErlangRangeException("Value too large for byte: " + val); } return i; } /** * Get the string representation of this number. * * @return the string representation of this number. */ @Override public String toString() { if (bigVal != null) { return "" + bigVal; } return "" + val; } /** * Convert this number to the equivalent Erlang external representation. * * @param buf * an output stream to which the encoded number should be written. */ @Override public void encode(final OtpOutputStream buf) { if (bigVal != null) { buf.write_big_integer(bigVal); } else { buf.write_long(val); } } /** * Determine if two numbers are equal. Numbers are equal if they contain the same * value. * * @param o * the number to compare to. * * @return true if the numbers have the same value. */ @Override public boolean equals(final Object o) { if (!(o instanceof OtpErlangLong)) { return false; } final OtpErlangLong that = (OtpErlangLong) o; if (bigVal != null && that.bigVal != null) { return bigVal.equals(that.bigVal); } else if (bigVal == null && that.bigVal == null) { return val == that.val; } return false; } @Override protected int doHashCode() { if (bigVal != null) { return bigVal.hashCode(); } return BigInteger.valueOf(val).hashCode(); } }