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;
}
}
}