/*
 * Copyright (c) 2002, 2013, 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 sun.jvm.hotspot.debugger.linux;

import sun.jvm.hotspot.debugger.*;

public class LinuxAddress implements Address {
    protected LinuxDebugger debugger;
    protected long addr;

    public LinuxAddress(LinuxDebugger debugger, long addr) {
        this.debugger = debugger;
        this.addr = addr;
    }

    //
    // Basic Java routines
    //

    public boolean equals(Object arg) {
        if (arg == null) {
            return false;
        }

        if (!(arg instanceof LinuxAddress)) {
            return false;
        }

        return (addr == ((LinuxAddress) arg).addr);
    }

    public int hashCode() {
        // FIXME: suggestions on a better hash code?
        return (int) addr;
    }

    public String toString() {
        return debugger.addressValueToString(addr);
    }

    //
    // C/C++-related routines
    //

    public long getCIntegerAt(long offset, long numBytes, boolean isUnsigned)
            throws UnalignedAddressException, UnmappedAddressException {
        return debugger.readCInteger(addr + offset, numBytes, isUnsigned);
    }

    public Address getAddressAt(long offset)
            throws UnalignedAddressException, UnmappedAddressException {
        return debugger.readAddress(addr + offset);
    }

    public Address getCompOopAddressAt(long offset)
            throws UnalignedAddressException, UnmappedAddressException {
        return debugger.readCompOopAddress(addr + offset);
    }

    public Address getCompKlassAddressAt(long offset)
            throws UnalignedAddressException, UnmappedAddressException {
        return debugger.readCompKlassAddress(addr + offset);
    }

    //
    // Java-related routines
    //

  public boolean getJBooleanAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJBoolean(addr + offset);
  }

  public byte getJByteAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJByte(addr + offset);
  }

  public char getJCharAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJChar(addr + offset);
  }

  public double getJDoubleAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJDouble(addr + offset);
  }

  public float getJFloatAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJFloat(addr + offset);
  }

  public int getJIntAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJInt(addr + offset);
  }

  public long getJLongAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJLong(addr + offset);
  }

  public short getJShortAt(long offset) throws UnalignedAddressException, UnmappedAddressException {
    return debugger.readJShort(addr + offset);
  }

  public OopHandle getOopHandleAt(long offset)
    throws UnalignedAddressException, UnmappedAddressException, NotInHeapException {
    return debugger.readOopHandle(addr + offset);
  }

  public OopHandle getCompOopHandleAt(long offset)
    throws UnalignedAddressException, UnmappedAddressException, NotInHeapException {
    return debugger.readCompOopHandle(addr + offset);
  }

  // Mutators -- not implemented for now (FIXME)
  public void setCIntegerAt(long offset, long numBytes, long value) {
    throw new DebuggerException("Unimplemented");
  }
  public void setAddressAt(long offset, Address value) {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJBooleanAt      (long offset, boolean value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJByteAt         (long offset, byte value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJCharAt         (long offset, char value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJDoubleAt       (long offset, double value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJFloatAt        (long offset, float value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJIntAt          (long offset, int value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJLongAt         (long offset, long value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setJShortAt        (long offset, short value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }
  public void       setOopHandleAt     (long offset, OopHandle value)
    throws UnmappedAddressException, UnalignedAddressException {
    throw new DebuggerException("Unimplemented");
  }

  //
  // Arithmetic operations -- necessary evil.
  //

  public Address    addOffsetTo       (long offset) throws UnsupportedOperationException {
    long value = addr + offset;
    if (value == 0) {
      return null;
    }
    return new LinuxAddress(debugger, value);
  }

  public OopHandle  addOffsetToAsOopHandle(long offset) throws UnsupportedOperationException {
    long value = addr + offset;
    if (value == 0) {
      return null;
    }
    return new LinuxOopHandle(debugger, value);
  }

  /** (FIXME: any signed/unsigned issues? Should this work for
      OopHandles?) */
  public long       minus(Address arg) {
    if (arg == null) {
      return addr;
    }
    return addr - ((LinuxAddress) arg).addr;
  }

  // Two's complement representation.
  // All negative numbers are larger than positive numbers.
  // Numbers with the same sign can be compared normally.
  // Test harness is below in main().

  public boolean    lessThan          (Address a) {
    if (a == null) {
      return false;
    }
    LinuxAddress arg = (LinuxAddress) a;
    if ((addr >= 0) && (arg.addr < 0)) {
      return true;
    }
    if ((addr < 0) && (arg.addr >= 0)) {
      return false;
    }
    return (addr < arg.addr);
  }

  public boolean    lessThanOrEqual   (Address a) {
    if (a == null) {
      return false;
    }
    LinuxAddress arg = (LinuxAddress) a;
    if ((addr >= 0) && (arg.addr < 0)) {
      return true;
    }
    if ((addr < 0) && (arg.addr >= 0)) {
      return false;
    }
    return (addr <= arg.addr);
  }

  public boolean    greaterThan       (Address a) {
    if (a == null) {
      return true;
    }
    LinuxAddress arg = (LinuxAddress) a;
    if ((addr >= 0) && (arg.addr < 0)) {
      return false;
    }
    if ((addr < 0) && (arg.addr >= 0)) {
      return true;
    }
    return (addr > arg.addr);
  }

  public boolean    greaterThanOrEqual(Address a) {
    if (a == null) {
      return true;
    }
    LinuxAddress arg = (LinuxAddress) a;
    if ((addr >= 0) && (arg.addr < 0)) {
      return false;
    }
    if ((addr < 0) && (arg.addr >= 0)) {
      return true;
    }
    return (addr >= arg.addr);
  }

  public Address    andWithMask(long mask) throws UnsupportedOperationException {
    long value = addr & mask;
    if (value == 0) {
      return null;
    }
    return new LinuxAddress(debugger, value);
  }

  public Address    orWithMask(long mask) throws UnsupportedOperationException {
    long value = addr | mask;
    if (value == 0) {
      return null;
    }
    return new LinuxAddress(debugger, value);
  }

  public Address    xorWithMask(long mask) throws UnsupportedOperationException {
    long value = addr ^ mask;
    if (value == 0) {
      return null;
    }
    return new LinuxAddress(debugger, value);
  }

  public long asLongValue() { return addr; }

  //--------------------------------------------------------------------------------
  // Internals only below this point
  //

  long getValue() {
    return addr;
  }


  private static void check(boolean arg, String failMessage) {
    if (!arg) {
      System.err.println(failMessage + ": FAILED");
      System.exit(1);
    }
  }

  // Test harness
  public static void main(String[] args) {
    // p/n indicates whether the interior address is really positive
    // or negative. In unsigned terms, p1 < p2 < n1 < n2.

    LinuxAddress p1 = new LinuxAddress(null, 0x7FFFFFFFFFFFFFF0L);
    LinuxAddress p2 = (LinuxAddress) p1.addOffsetTo(10);
    LinuxAddress n1 = (LinuxAddress) p2.addOffsetTo(10);
    LinuxAddress n2 = (LinuxAddress) n1.addOffsetTo(10);

    // lessThan positive tests
    check(p1.lessThan(p2), "lessThan 1");
    check(p1.lessThan(n1), "lessThan 2");
    check(p1.lessThan(n2), "lessThan 3");
    check(p2.lessThan(n1), "lessThan 4");
    check(p2.lessThan(n2), "lessThan 5");
    check(n1.lessThan(n2), "lessThan 6");

    // lessThan negative tests
    check(!p1.lessThan(p1), "lessThan 7");
    check(!p2.lessThan(p2), "lessThan 8");
    check(!n1.lessThan(n1), "lessThan 9");
    check(!n2.lessThan(n2), "lessThan 10");

    check(!p2.lessThan(p1), "lessThan 11");
    check(!n1.lessThan(p1), "lessThan 12");
    check(!n2.lessThan(p1), "lessThan 13");
    check(!n1.lessThan(p2), "lessThan 14");
    check(!n2.lessThan(p2), "lessThan 15");
    check(!n2.lessThan(n1), "lessThan 16");

    // lessThanOrEqual positive tests
    check(p1.lessThanOrEqual(p1), "lessThanOrEqual 1");
    check(p2.lessThanOrEqual(p2), "lessThanOrEqual 2");
    check(n1.lessThanOrEqual(n1), "lessThanOrEqual 3");
    check(n2.lessThanOrEqual(n2), "lessThanOrEqual 4");

    check(p1.lessThanOrEqual(p2), "lessThanOrEqual 5");
    check(p1.lessThanOrEqual(n1), "lessThanOrEqual 6");
    check(p1.lessThanOrEqual(n2), "lessThanOrEqual 7");
    check(p2.lessThanOrEqual(n1), "lessThanOrEqual 8");
    check(p2.lessThanOrEqual(n2), "lessThanOrEqual 9");
    check(n1.lessThanOrEqual(n2), "lessThanOrEqual 10");

    // lessThanOrEqual negative tests
    check(!p2.lessThanOrEqual(p1), "lessThanOrEqual 11");
    check(!n1.lessThanOrEqual(p1), "lessThanOrEqual 12");
    check(!n2.lessThanOrEqual(p1), "lessThanOrEqual 13");
    check(!n1.lessThanOrEqual(p2), "lessThanOrEqual 14");
    check(!n2.lessThanOrEqual(p2), "lessThanOrEqual 15");
    check(!n2.lessThanOrEqual(n1), "lessThanOrEqual 16");

    // greaterThan positive tests
    check(n2.greaterThan(p1), "greaterThan 1");
    check(n2.greaterThan(p2), "greaterThan 2");
    check(n2.greaterThan(n1), "greaterThan 3");
    check(n1.greaterThan(p1), "greaterThan 4");
    check(n1.greaterThan(p2), "greaterThan 5");
    check(p2.greaterThan(p1), "greaterThan 6");

    // greaterThan negative tests
    check(!p1.greaterThan(p1), "greaterThan 7");
    check(!p2.greaterThan(p2), "greaterThan 8");
    check(!n1.greaterThan(n1), "greaterThan 9");
    check(!n2.greaterThan(n2), "greaterThan 10");

    check(!p1.greaterThan(n2), "greaterThan 11");
    check(!p2.greaterThan(n2), "greaterThan 12");
    check(!n1.greaterThan(n2), "greaterThan 13");
    check(!p1.greaterThan(n1), "greaterThan 14");
    check(!p2.greaterThan(n1), "greaterThan 15");
    check(!p1.greaterThan(p2), "greaterThan 16");

    // greaterThanOrEqual positive tests
    check(p1.greaterThanOrEqual(p1), "greaterThanOrEqual 1");
    check(p2.greaterThanOrEqual(p2), "greaterThanOrEqual 2");
    check(n1.greaterThanOrEqual(n1), "greaterThanOrEqual 3");
    check(n2.greaterThanOrEqual(n2), "greaterThanOrEqual 4");

    check(n2.greaterThanOrEqual(p1), "greaterThanOrEqual 5");
    check(n2.greaterThanOrEqual(p2), "greaterThanOrEqual 6");
    check(n2.greaterThanOrEqual(n1), "greaterThanOrEqual 7");
    check(n1.greaterThanOrEqual(p1), "greaterThanOrEqual 8");
    check(n1.greaterThanOrEqual(p2), "greaterThanOrEqual 9");
    check(p2.greaterThanOrEqual(p1), "greaterThanOrEqual 10");

    // greaterThanOrEqual negative tests
    check(!p1.greaterThanOrEqual(n2), "greaterThanOrEqual 11");
    check(!p2.greaterThanOrEqual(n2), "greaterThanOrEqual 12");
    check(!n1.greaterThanOrEqual(n2), "greaterThanOrEqual 13");
    check(!p1.greaterThanOrEqual(n1), "greaterThanOrEqual 14");
    check(!p2.greaterThanOrEqual(n1), "greaterThanOrEqual 15");
    check(!p1.greaterThanOrEqual(p2), "greaterThanOrEqual 16");

    System.err.println("LinuxAddress: all tests passed successfully.");
  }
}
