package com.sleepycat.je.cleaner;
import com.sleepycat.je.utilint.Tracer;
import de.ovgu.cide.jakutil.*;
/** 
 * List of LSN offsets as a linked list of segments.  The reasons for using a
 * list of this type and not a java.util.List are:
 * <ul>
 * <li>Segements reduce memory overhead by storing long primitives rather than
 * Long objects.  Many longs per segment reduce link overhead.</li>
 * <li>Memory is only allocated for new segments, reducing the number of calls
 * to update the memory budget.</li>
 * <li>This is an append-only list that supports a single appender thread and
 * multiple unsynchronized reader threads.  The caller is responsible for
 * synchronizing such that only one thread calls add() at one time.  The reader
 * threads see data as it is changing but do not see inconsistent data (corrupt
 * longs) and do not require synchronization for thread safety.</li>
 * </ul>
 * <p>The algorithms here use traversal of the list segments rather than
 * recursion to avoid using a lot of stack space.</p>
 */
public class OffsetList {
  static final int SEGMENT_CAPACITY=100;
  private Segment head;
  private Segment tail;
  private int size;
  public OffsetList(){
    head=new Segment();
    tail=head;
  }
  /** 
 * Adds the given value and returns whether a new segment was allocated.
 */
  public boolean add(  long value,  boolean checkDupOffsets){
    if (checkDupOffsets) {
      assert (!contains(value)) : Tracer.getStackTrace(new Exception("Dup Offset " + Long.toHexString(value)));
    }
    Segment oldTail=tail;
    tail=tail.add(value);
    size+=1;
    return tail != oldTail;
  }
  public int size(){
    return size;
  }
  /** 
 * Merges the given list and returns whether a segment was freed.
 */
  boolean merge(  OffsetList other){
    boolean oneSegFreed=true;
    Segment seg=other.head;
    while (true) {
      Segment next=seg.next();
      if (next != null) {
        seg.setNext(head);
        head=seg;
        seg=next;
      }
 else {
        for (int i=0; i < seg.size(); i+=1) {
          if (add(seg.get(i),false)) {
            assert oneSegFreed;
            oneSegFreed=false;
          }
        }
        break;
      }
    }
    return oneSegFreed;
  }
  /** 
 * Returns an array of all values as longs.  If a writer thread is
 * appending to the list while this method is excuting, some values may be
 * missing from the returned array, but the operation is safe.
 */
  public long[] toArray(){
    long[] a=new long[size];
    int next=0;
    segments:     for (Segment seg=head; seg != null; seg=seg.next()) {
      for (int i=0; i < seg.size(); i+=1) {
        if (next >= a.length) {
          break segments;
        }
        a[next]=seg.get(i);
        next+=1;
      }
    }
    return a;
  }
  /** 
 * Returns whether this list contains the given offset.
 */
  boolean contains(  long offset){
    for (Segment seg=head; seg != null; seg=seg.next()) {
      for (int i=0; i < seg.size(); i+=1) {
        if (seg.get(i) == offset) {
          return true;
        }
      }
    }
    return false;
  }
  /** 
 * One segment of a OffsetList containing at most SEGMENT_CAPACITY values.
 * public for Sizeof.
 */
public static class Segment {
    private int index;
    private Segment next;
    private int[] values;
    public Segment(){
      values=new int[SEGMENT_CAPACITY];
    }
    /** 
 * Call this method on the tail.  The new tail is returned, if
 * allocating a new tail is necessary.
 */
    Segment add(    long value){
      if (index < values.length) {
        values[index]=(int)value;
        index+=1;
        return this;
      }
 else {
        Segment seg=new Segment();
        seg.values[0]=(int)value;
        seg.index=1;
        next=seg;
        return seg;
      }
    }
    /** 
 * Returns the value at the given index from this segment only.
 */
    long get(    int i){
      return ((long)values[i]) & 0xFFFFFFFF;
    }
    /** 
 * Returns the next segment or null if this is the tail segment.
 */
    Segment next(){
      return next;
    }
    /** 
 * Sets the next pointer during a merge.
 */
    void setNext(    Segment next){
      this.next=next;
    }
    /** 
 * Returns the number of values in this segment.
 */
    int size(){
      return index;
    }
  }
}
