/*
 * (C) Copyright Department of Computer Science,
 * Australian National University. 2002
 */

package s3.core.services.memory.JMTk;

import ovm.core.services.memory.VM_Address;
import ovm.core.services.memory.VM_Word;
import s3.core.services.memory.vmInterface.Constants;

/**
 * This class implements a simple bump pointer allocator.  The
 * allocator operates in <code>BLOCK</code> sized units.  Intra-block
 * allocation is fast, requiring only a load, addition comparison and
 * store.  If a block boundary is encountered the allocator will
 * request more memory (virtual and actual).
 *
 * @author <a href="http://cs.anu.edu.au/~Steve.Blackburn">Steve Blackburn</a>
 * @version $Revision: 1.7 $
 * @date $Date: 2004/05/28 20:25:01 $
 */
final class BumpPointer extends Allocator 
  implements Constants, VM_Uninterruptible {
  public final static String Id = "$Id: BumpPointer.java,v 1.7 2004/05/28 20:25:01 jv Exp $"; 

  /**
   * Constructor
   *
   * @param vmr The virtual memory resource from which this bump
   * pointer will acquire virtual memory.
   * @param mr The memory resource from which this bump pointer will
   * acquire memory.
   */
  BumpPointer(VMResource vmr, MemoryResource mr) {
    vmResource = vmr;
    memResource = mr;
    reset();
    initialSize = 0;
  }

  BumpPointer(VMResource vmr, MemoryResource mr, long size){
    vmResource = vmr;
    memResource = mr;
    reset();
    initialSize = size;
  }

  public void reset () {
    cursor = INITIAL_CURSOR_VALUE;
    limit = INITIAL_LIMIT_VALUE;
  }

  public void setCursor(VM_Address location){
    cursor = location;
  }

  public int memoryConsumed(VM_Address start){
    return cursor.sub(start.asWord()).asInt();
  }

  /**
   * Re-associate this bump pointer with a different virtual memory
   * resource.  Reset the bump pointer so that it will use this virtual
   * memory resource on the next call to <code>alloc</code>.
   *
   * @param vmr The virtual memory resouce with which this bump
   * pointer is to be associated.
   */
  public void rebind(VMResource vmr) {
    reset();
    vmResource = vmr;
  }

  /**
   * Allocate space for a new object.  This is frequently executed code and 
   * the coding is deliberaetly sensitive to the optimizing compiler.
   * After changing this, always check the IR/MC that is generated.
   *
   * @param isScalar Is the object to be allocated a scalar (or array)?
   * @param bytes The number of bytes allocated
   * @return The address of the first byte of the allocated region
   */
  final public VM_Address alloc(boolean isScalar, int bytes) 
    throws VM_PragmaInline {
    VM_Address oldCursor = cursor;
    VM_Address newCursor = oldCursor.add(bytes);
    if (useLimit) {
      if (newCursor.uGT(limit))
	return allocSlow(isScalar, bytes);
    } else {
      VM_Word tmp = oldCursor.asWord().xor(newCursor.asWord());
      if (tmp.uGT(VM_Word.fromInt(TRIGGER)))
	return allocSlow(isScalar, bytes);
    }
    cursor = newCursor;
    return oldCursor;
  }

  final protected VM_Address allocSlowOnce(boolean isScalar, int bytes, 
					   boolean inGC) {
    long acquiresize = bytes>initialSize ? bytes : initialSize;
    int chunkSize = ((int)((acquiresize + CHUNK_SIZE - 1) >>> LOG_CHUNK_SIZE) << LOG_CHUNK_SIZE);
    VM_Address start = vmResource.acquire(Conversions.bytesToPages(chunkSize), memResource);
    if (start.isNull())
      return start;
    Memory.zero(start, VM_Word.fromInt(chunkSize));

    // check for (dis)contiguity with previous chunk
    if (limit.NE(start)) cursor = start;
    limit = start.add(chunkSize);
    return alloc(isScalar, bytes);
  }

  public void show() {
    //Log.write("cursor = "); Log.write(cursor);
    //Log.write(" limit = "); Log.writeln(limit);
  }

  /****************************************************************************
   *
   * Instance variables
   */
  private VM_Address cursor;
  private VM_Address limit;
  private VMResource vmResource;
  private MemoryResource memResource;
  private long initialSize;

  /****************************************************************************
   *
   * Final class variables (aka constants)
   *
   * Must ensure the bump pointer will go through slow path on (first)
   * alloc of initial value
   */
  private static final int LOG_CHUNK_SIZE = VMResource.LOG_BYTES_IN_PAGE + 3;
  private static final int CHUNK_SIZE = 1 << LOG_CHUNK_SIZE;
  private static final int TRIGGER = CHUNK_SIZE - 1;
  private static final VM_Address INITIAL_CURSOR_VALUE = VM_Address.fromInt(TRIGGER);
  private static final VM_Address INITIAL_LIMIT_VALUE = INITIAL_CURSOR_VALUE;
  private static final boolean useLimit = true;
}


