/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.unsafe;
import static com.sun.max.vm.MaxineVM.*;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
/**
* Offsets from addresses or pointers. Unlike an 'Address', an 'Offset' can be negative. Both types have the identical
* number of bits used for representation. However, 'Offset' uses twos complement, whereas 'Address' is simply unsigned.
*/
public class Offset extends Word {
@HOSTED_ONLY
public Offset(long value) {
super(value);
}
@INLINE
public static Offset zero() {
return isHosted() ? ZERO : fromInt(0);
}
@INLINE
public static Offset fromUnsignedInt(int value) {
return Address.fromUnsignedInt(value).asOffset();
}
@INLINE
public static Offset fromInt(int value) {
if (isHosted()) {
return fromLong(value);
}
if (Word.width() == 64) {
long n = value;
return UnsafeCast.asOffset(n);
}
return UnsafeCast.asOffset(value);
}
@INLINE
public static Offset fromLong(long value) {
if (isHosted()) {
if (value == 0) {
return ZERO;
}
if (value >= Cache.LOWEST_VALUE && value <= Cache.HIGHEST_VALUE) {
return Cache.cache[(int) value - Cache.LOWEST_VALUE];
}
return new Offset(value);
}
if (Word.width() == 64) {
return UnsafeCast.asOffset(value);
}
int n = (int) value;
return UnsafeCast.asOffset(n);
}
@Override
@HOSTED_ONLY
public String toString() {
return "&" + toHexString();
}
@INLINE
public int toInt() {
if (isHosted()) {
return (int) value;
}
if (Word.width() == 64) {
long n = UnsafeCast.asLong(this);
return (int) n;
}
return UnsafeCast.asInt(this);
}
@INLINE
public long toLong() {
if (isHosted()) {
return value;
}
if (Word.width() == 64) {
return UnsafeCast.asLong(this);
}
return UnsafeCast.asInt(this);
}
@INLINE
public int compareTo(Offset other) {
if (greaterThan(other)) {
return 1;
}
if (lessThan(other)) {
return -1;
}
return 0;
}
@INLINE
public boolean equals(int other) {
if (isHosted()) {
return toLong() == other;
}
return fromInt(other) == this;
}
@INLINE
public boolean lessEqual(Offset other) {
if (Word.width() == 64) {
return toLong() <= other.toLong();
}
return toInt() <= other.toInt();
}
@INLINE
public boolean lessEqual(int other) {
return lessEqual(fromInt(other));
}
@INLINE
public boolean lessThan(Offset other) {
if (Word.width() == 64) {
return toLong() < other.toLong();
}
return toInt() < other.toInt();
}
@INLINE
public boolean lessThan(int other) {
return lessThan(fromInt(other));
}
@INLINE
public boolean greaterEqual(Offset other) {
if (Word.width() == 64) {
return toLong() >= other.toLong();
}
return toInt() >= other.toInt();
}
@INLINE
public boolean greaterEqual(int other) {
return greaterEqual(fromInt(other));
}
@INLINE
public boolean greaterThan(Offset other) {
if (Word.width() == 64) {
return toLong() > other.toLong();
}
return toInt() > other.toInt();
}
@INLINE
public boolean greaterThan(int other) {
return greaterThan(fromInt(other));
}
@INLINE
public Offset negate() {
if (Word.width() == 64) {
return fromLong(-toLong());
}
return fromInt(-toInt());
}
@INLINE
public boolean isNegative() {
if (Word.width() == 64) {
return toLong() < 0L;
}
return toInt() < 0;
}
@INLINE
public Offset plus(Offset addend) {
if (Word.width() == 64) {
return fromLong(toLong() + addend.toLong());
}
return fromInt(toInt() + addend.toInt());
}
@INLINE
public Offset plus(Size addend) {
return plus(addend.asOffset());
}
@INLINE
public Offset plus(int addend) {
return plus(fromInt(addend));
}
@INLINE
public Offset plus(long addend) {
return plus(fromLong(addend));
}
@INLINE
public Offset minus(Offset subtrahend) {
if (Word.width() == 64) {
return fromLong(toLong() - subtrahend.toLong());
}
return fromInt(toInt() - subtrahend.toInt());
}
@INLINE
public Offset minus(Size subtrahend) {
return minus(subtrahend.asOffset());
}
@INLINE
public Offset minus(int subtrahend) {
return minus(fromInt(subtrahend));
}
@INLINE
public Offset minus(long subtrahend) {
return minus(fromLong(subtrahend));
}
@INLINE
public Offset times(Offset factor) {
if (Word.width() == 64) {
return fromLong(toLong() * factor.toLong());
}
return fromInt(toInt() * factor.toInt());
}
@INLINE
public Offset times(Address factor) {
return times(factor.asOffset());
}
@INLINE
public Offset times(int factor) {
return times(fromInt(factor));
}
@INLINE
public Offset dividedBy(Offset divisor) throws ArithmeticException {
if (Word.width() == 64) {
return fromLong(toLong() / divisor.toLong());
}
return fromInt(toInt() / divisor.toInt());
}
@INLINE
public Offset dividedBy(int divisor) throws ArithmeticException {
return dividedBy(fromInt(divisor));
}
@INLINE
public Offset remainder(Offset divisor) throws ArithmeticException {
if (Word.width() == 64) {
return fromLong(toLong() % divisor.toLong());
}
return fromInt(toInt() % divisor.toInt());
}
@INLINE
public int remainder(int divisor) throws ArithmeticException {
return remainder(fromInt(divisor)).toInt();
}
@INLINE
public boolean isRoundedBy(int numberOfBytes) {
return remainder(numberOfBytes) == 0;
}
@INLINE
public Offset roundedUpBy(int numberOfBytes) {
if (isRoundedBy(numberOfBytes)) {
return this;
}
return plus(numberOfBytes - remainder(numberOfBytes));
}
@INLINE
public Offset and(Offset operand) {
if (Word.width() == 64) {
return fromLong(toLong() & operand.toLong());
}
return fromInt(toInt() & operand.toInt());
}
@INLINE
public Offset and(int operand) {
return and(fromInt(operand));
}
@INLINE
public Offset and(long operand) {
return and(fromLong(operand));
}
@INLINE
public Offset or(Offset operand) {
if (Word.width() == 64) {
return fromLong(toLong() | operand.toLong());
}
return fromInt(toInt() | operand.toInt());
}
@INLINE
public Offset or(int operand) {
return or(fromInt(operand));
}
@INLINE
public Offset or(long operand) {
return or(fromLong(operand));
}
@INLINE
public Offset not() {
if (Word.width() == 64) {
return fromLong(~toLong());
}
return fromInt(~toInt());
}
@INLINE
public Offset roundedDownBy(int numberOfBytes) {
return minus(remainder(numberOfBytes));
}
@INLINE
public Offset aligned() {
int n = Word.size();
return plus(n - 1).and(Offset.fromInt(n - 1).not());
}
@INLINE
public boolean isAligned() {
int n = Word.size();
return and(n - 1).equals(Offset.zero());
}
@HOSTED_ONLY
public int numberOfEffectiveBits() {
if (Word.width() == 64) {
long n = toLong();
if (n >= 0) {
return 64 - Long.numberOfLeadingZeros(n);
}
return 65 - Long.numberOfLeadingZeros(~n);
}
int n = toInt();
if (n >= 0) {
return 32 - Integer.numberOfLeadingZeros(n);
}
return 33 - Integer.numberOfLeadingZeros(~n);
}
@HOSTED_ONLY
public WordWidth effectiveWidth() {
int bit = numberOfEffectiveBits();
for (WordWidth width : WordWidth.values()) {
if (bit < width.numberOfBits) {
return width;
}
}
throw ProgramError.unexpected();
}
@HOSTED_ONLY
public static Offset ZERO = new Offset(0);
@HOSTED_ONLY
private static class Cache {
private Cache() {
}
static int LOWEST_VALUE = -100;
static int HIGHEST_VALUE = 1000;
static Offset[] cache = new Offset[(HIGHEST_VALUE - LOWEST_VALUE) + 1];
static {
for (int i = 0; i < cache.length; i++) {
cache[i] = new Offset(i + LOWEST_VALUE);
}
}
}
}