/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package sun.jvm.hotspot.memory;

import java.util.*;

import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.utilities.*;
import sun.jvm.hotspot.utilities.Observable;
import sun.jvm.hotspot.utilities.Observer;

/** A very simple data structure representing a contiguous region of
    address space. */

public class MemRegion implements Cloneable {
  private Address start;
  private long byteSize;

  private static AddressField  startField;
  private static CIntegerField wordSizeField;

  static {
    VM.registerVMInitializedObserver(new Observer() {
        public void update(Observable o, Object data) {
          initialize(VM.getVM().getTypeDataBase());
        }
      });
  }

  private static synchronized void initialize(TypeDataBase db) {
    Type type = db.lookupType("MemRegion");

    startField    = type.getAddressField("_start");
    wordSizeField = type.getCIntegerField("_word_size");
  }

  public MemRegion() {
  }

  /** This constructor takes a "MemRegion*" in the target process */
  public MemRegion(Address memRegionAddr) {
    this(startField.getValue(memRegionAddr),
         wordSizeField.getValue(memRegionAddr));
  }

  public MemRegion(Address start, long wordSize) {
    setStart(start);
    setWordSize(wordSize);
  }

  public MemRegion(Address start, Address limit) {
    setStart(start);
    byteSize = limit.minus(start);
  }

  public Object clone() {
    return new MemRegion(start, byteSize);
  }

  public MemRegion copy() {
    return (MemRegion) clone();
  }

  public MemRegion intersection(MemRegion mr2) {
    MemRegion res = new MemRegion();
    if (AddressOps.gt(mr2.start(), start())) {
      res.setStart(mr2.start());
    } else {
      res.setStart(start());
    }
    Address resEnd;
    Address end = end();
    Address mr2End = mr2.end();
    if (AddressOps.lt(end, mr2End)) {
      resEnd = end;
    } else {
      resEnd = mr2End;
    }
    if (AddressOps.lt(resEnd, res.start())) {
      res.setStart(null);
      res.setWordSize(0);
    } else {
      res.setEnd(resEnd);
    }
    return res;
  }

  public MemRegion union(MemRegion mr2) {
    MemRegion res = new MemRegion();
    if (AddressOps.lt(mr2.start(), start())) {
      res.setStart(mr2.start());
    } else {
      res.setStart(start());
    }
    Address resEnd;
    Address end = end();
    Address mr2End = mr2.end();
    if (AddressOps.gt(end, mr2End)) {
      resEnd = end;
    } else {
      resEnd = mr2End;
    }
    res.setEnd(resEnd);
    return res;
  }

  public Address start() {
    return start;
  }

  public OopHandle startAsOopHandle() {
    return start().addOffsetToAsOopHandle(0);
  }

  public Address end() {
    return start.addOffsetTo(byteSize);
  }

  public OopHandle endAsOopHandle() {
    return end().addOffsetToAsOopHandle(0);
  }

  public void setStart(Address start) {
    this.start = start;
  }

  public void setEnd(Address end) {
    byteSize = end.minus(start);
  }

  public void setWordSize(long wordSize) {
    byteSize = VM.getVM().getAddressSize() * wordSize;
  }

  public boolean contains(MemRegion mr2) {
    return AddressOps.lte(start, mr2.start) && AddressOps.gte(end(), mr2.end());
  }

  public boolean contains(Address addr) {
    return AddressOps.gte(addr, start()) && AddressOps.lt(addr, end());
  }

  public long byteSize() {
    return byteSize;
  }

  public long wordSize() {
    return byteSize / VM.getVM().getAddressSize();
  }
}
