/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* 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 erjang;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.List;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public final class ESmall extends EInteger {
private static final Type ESMALL_TYPE = Type.getType(ESmall.class);
public static final ESmall ZERO = new ESmall(0);
public static final ESmall MINUS_ONE = new ESmall(-1);
public static final ESmall ONE = new ESmall(1);
public final int value;
final static int PREALLOC_COUNT = 0xff; // Must be at least 256.
static final ESmall[] little = new ESmall[PREALLOC_COUNT+1];
static {
for (int i = 0; i < little.length; i++) {
little[i] = new ESmall(i);
}
}
@Override
public byte[] encode_unsigned() {
if (value < 0) throw ERT.badarg(this);
if (value < 0x100) {
return new byte[]{
(byte)( value & 0xff )
};
}
if (value < 0x10000) {
return new byte[]{
(byte)((value&0xff00) >> 8),
(byte)( value&0x00ff )
};
}
if (value < 0x1000000) {
return new byte[]{
(byte)((value&0xff0000) >> 16),
(byte)((value&0x00ff00) >> 8),
(byte)( value&0x0000ff )
};
}
return new byte[]{
(byte)((value&0xff000000) >>> 24),
(byte)((value&0x00ff0000) >>> 16),
(byte)((value&0x0000ff00) >>> 8),
(byte)( value&0x000000ff )
};
}
@Override
public ESmall testSmall() {
return this;
}
public ESmall(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
public long longValue() {
return value;
}
public EInteger inc() {
if (value==Integer.MAX_VALUE) {
return ERT.box((long)Integer.MAX_VALUE + 1L);
} else {
return ERT.box(value+1);
}
}
public EInteger dec() {
if (value==Integer.MIN_VALUE) {
return ERT.box((long)Integer.MIN_VALUE - 1L);
} else {
return ERT.box(value-1);
}
}
public boolean is_zero() { return value==0; }
@Override
public void visitIOList(EIOListVisitor out) throws ErlangError {
if ((value & 0xff) != value)
throw ERT.badarg();
out.visit(value);
}
@Override
public boolean collectIOList(List<ByteBuffer> out) {
ByteBuffer b = ByteBuffer.allocate(1);
b.put(0, (byte) value);
out.add(b);
return true;
}
public boolean equals(EObject other) {
if (other == this) return true;
if (other instanceof ESmall)
return ((ESmall)other).value == value;
if (other instanceof EDouble)
return ((EDouble)other).value == value;
return false;
}
@Override
int compare_same(EObject rhs) {
return rhs.r_compare_same(this);
}
int r_compare_same(ESmall lhs) {
if (lhs.value < value)
return -1;
if (lhs.value == value)
return 0;
return 1;
}
int r_compare_same(EBig lhs) {
return lhs.value.compareTo(BigInteger.valueOf(value));
}
int r_compare_same(EDouble lhs) {
return lhs.value < value ? -1 : lhs.value == value ? 0 : 1;
}
@Override
public boolean equalsExactly(EObject rhs) {
return rhs.r_equals_exactly(this);
}
boolean r_equals_exactly(ESmall lhs) {
return lhs.value == value;
}
/*
* (non-Javadoc)
*
* @see erjang.EObject#asInt()
*/
@Override
public int asInt() {
return value;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ESmall) {
ESmall o = (ESmall) obj;
return o.value == value;
}
return false;
}
@Override
public int intValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@Override
public org.objectweb.asm.Type emit_const(MethodVisitor fa) {
fa.visitLdcInsn(new Integer(value));
fa.visitMethodInsn(Opcodes.INVOKESTATIC, ESMALL_TYPE.getInternalName(),
"make", "(I)Lerjang/ESmall;");
Type type = ESMALL_TYPE;
return type;
}
@Override
public EInteger abs() {
// OBS: abs(Integer.MIN_VALUE) cannot be represented by an ESmall.
return ERT.box(Math.abs((long)value));
}
/**
* @param v
* @return
*/
public static ESmall make(int v) {
if ((v & PREALLOC_COUNT)==v) return little[v];
else return new ESmall(v);
}
//
// Arithmetic
//
public double doubleValue() {
return value;
}
public BigInteger bigintValue() {
return BigInteger.valueOf(value);
}
public ENumber add(EObject other, boolean guard) {
return other.add(value, guard);
}
@BIF(name="+")
public ENumber add(EObject rhs) {
return rhs.add(value, false);
}
public EInteger add(int b, boolean guard) {
int a = value;
int r=a+b;
if (( (a^r) & (b^r) ) < 0)
return ERT.box((long)a + (long)b);
return ERT.box(r);
}
/** overflow check due to http://www.drdobbs.com/jvm/signalling-integer-overflows-in-java/210500001 */
@BIF(name="+")
public EInteger add(ESmall rhs) {
int a = value;
int b = rhs.value;
int r=a+b;
if (( (a^r) & (b^r) ) < 0)
return ERT.box((long)a + (long)b);
return ERT.box(r);
}
public ENumber add(double lhs, boolean guard) {
return ERT.box(lhs + value);
}
public ENumber add(BigInteger lhs, boolean guard) {
return ERT.box(lhs.add(BigInteger.valueOf(value)));
}
/* subtract */
public ENumber subtract(EObject other, boolean guard) {
return other.r_subtract(value, guard);
}
public ENumber subtract(ESmall rhs) {
return ERT.box((long)value - (long)rhs.value);
}
@Deprecated
public ENumber subtract(int rhs) {
return ERT.box((long)value - rhs);
}
public ENumber r_subtract(int lhs, boolean guard) {
return ERT.box((long) lhs - (long) value);
}
public ENumber r_subtract(double lhs, boolean guard) {
return ERT.box(lhs - value);
}
public ENumber r_subtract(BigInteger lhs, boolean guard) {
return ERT.box(lhs.subtract(BigInteger.valueOf(value)));
}
// integer division erlang:'*'/2
@BIF(name="*")
public ENumber multiply(EObject other) {
return other.r_multiply(value);
}
public EInteger r_multiply(int lhs) {
return ERT.box((long) lhs * (long) value);
}
public EDouble r_multiply(double lhs) {
return ERT.box(lhs * value);
}
public EInteger r_multiply(BigInteger lhs) {
return ERT.box(lhs.multiply(BigInteger.valueOf(value)));
}
@Override
public ENumber negate() {
return new ESmall(-value);
}
// integer division erlang:div/2
public EDouble divide(EObject other) {
return other.r_divide(value);
}
public EDouble r_divide(int lhs) {
if (value==0) throw ERT.badarith(lhs, this);
return ERT.box((double) lhs / value);
}
public EDouble r_divide(double lhs) {
if (value==0) throw ERT.badarith(ERT.box(lhs), this);
return ERT.box(lhs / value);
}
public EDouble r_divide(BigInteger lhs) {
if (value==0) throw ERT.badarith(ERT.box(lhs), this);
return ERT.box(lhs.doubleValue() / value);
}
// integer division erlang:div/2
public EInteger idiv(EObject other) {
return other.r_idiv(value);
}
public EInteger idiv(int rhs) {
if (rhs==0) throw ERT.badarith(this, ERT.box(rhs));
return ERT.box(value / rhs);
}
public EInteger r_idiv(int lhs) {
if (value==0) throw ERT.badarith(ERT.box(lhs), this);
return ERT.box((long) lhs / (long) value);
}
public EInteger r_idiv(BigInteger lhs) {
if (value==0) throw ERT.badarith(lhs, this);
return ERT.box(lhs.divide(BigInteger.valueOf(value)));
}
// remainder erlang:rem/2
public ESmall irem(int rhs) {
if (rhs==0) throw ERT.badarith(this, ERT.box(rhs));
return ERT.box(value % rhs);
}
public EInteger irem(EObject other) {
return other.r_irem(value);
}
public EInteger r_irem(int lhs) {
if (value==0) throw ERT.badarith(ERT.box(lhs), this);
return ERT.box(lhs % value);
}
public EInteger r_irem(BigInteger lhs) {
if (value==0) throw ERT.badarith(ERT.box(lhs), this);
return ERT.box(lhs.remainder(BigInteger.valueOf(value)));
}
// shift right erlang:shr/2
public EInteger bsr(EObject other) {
return other.r_bsr(value);
}
public EInteger r_bsr(int lhs) {
/** it's a small positive integer in the range 0..31 */
if ((value & ~31) == 0)
return ERT.box((lhs >> value));
if (value >= 32) {
if (lhs < 0)
return ESmall.MINUS_ONE;
else
return ZERO;
} else /* if (value < 0) */ {
return ERT.box(BigInteger.valueOf(lhs).shiftRight(value));
}
}
public EInteger r_bsr(BigInteger lhs) {
return ERT.box(lhs.shiftRight(value));
}
// shift right erlang:shl/2
public EInteger bsl(EObject other) {
return other.r_bsl(value);
}
public EInteger r_bsl(int lhs) {
if ((value & ~31) == 0)
return ERT.box(((long)lhs) << value);
return ERT.box(BigInteger.valueOf(lhs).shiftLeft(value));
}
public EInteger r_bsl(BigInteger lhs) {
return ERT.box(lhs.shiftLeft(value));
}
// binary and - erlang:band/2
public EInteger band(EObject other) {
return other.band(value);
}
public EInteger band(int lhs) {
return ERT.box((lhs & value));
}
public EInteger band(BigInteger lhs) {
return ERT.box(lhs.and(BigInteger.valueOf(value)));
}
// binary or - erlang:band/2
public EInteger bor(EObject other) {
return other.bor(value);
}
public EInteger bor(int lhs) {
return ERT.box((lhs | value));
}
public EInteger bor(BigInteger lhs) {
return ERT.box(lhs.or(BigInteger.valueOf(value)));
}
// binary xor - erlang:band/2
public EInteger bxor(EObject other) {
return other.bxor(value);
}
public EInteger bxor(int lhs) {
return ERT.box((lhs ^ value));
}
public EInteger bxor(BigInteger lhs) {
return ERT.box(lhs.xor(BigInteger.valueOf(value)));
}
public EInteger bnot() {
return ERT.box(~value);
}
@Override
public void encode(EOutputStream eos) {
eos.write_int(value);
}
}