package com.sleepycat.je.cleaner; import java.nio.ByteBuffer; import java.util.Arrays; import com.sleepycat.je.log.LogReadable; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.log.LogWritable; import de.ovgu.cide.jakutil.*; /** * Stores a sorted list of LSN offsets in a packed short representation. Each * stored value is the difference between two consecutive offsets. The stored * values are stored as one or more shorts where each short holds 0x7fff * values. Shorts are in LSB order. The value is negated if more shorts for * the same offset follow; this works because offsets are always positive * values. */ public class PackedOffsets implements LogWritable, LogReadable { private short[] data; private int size; /** * Creates an empty object. */ public PackedOffsets(){ } /** * Returns an iterator over all offsets. */ Iterator iterator(){ return new Iterator(); } /** * Packs the given offsets, replacing any offsets stored in this object. */ public void pack( long[] offsets){ short[] newData=new short[offsets.length * 3]; Arrays.sort(offsets); int dataIndex=0; long priorVal=0; for (int i=0; i < offsets.length; i+=1) { long val=offsets[i]; dataIndex=append(newData,dataIndex,val - priorVal); priorVal=val; } data=new short[dataIndex]; System.arraycopy(newData,0,data,0,dataIndex); size=offsets.length; } /** * Returns the unpacked offsets. */ long[] toArray(){ long[] offsets=new long[size]; int index=0; Iterator iter=iterator(); while (iter.hasNext()) { offsets[index++]=iter.next(); } assert index == size; return offsets; } /** * Copies the given value as a packed long to the array starting at the * given index. Returns the index of the next position in the array. */ private int append( short[] to, int index, long val){ assert val >= 0; while (true) { short s=(short)(val & 0x7fff); val>>>=15; if (val > 0) { to[index++]=(short)(-1 - s); } else { to[index++]=s; break; } } return index; } /** * An iterator over all offsets. */ class Iterator { private int index; private long priorVal; private Iterator(){ } boolean hasNext(){ return data != null && index < data.length; } long next(){ long val=priorVal; for (int shift=0; ; shift+=15) { long s=data[index++]; if (s < 0) { val+=(-1 - s) << shift; } else { val+=s << shift; break; } } priorVal=val; return val; } } /** * @see LogWritable#getLogSize */ public int getLogSize(){ return (2 * LogUtils.getIntLogSize()) + ((data != null) ? (data.length * LogUtils.SHORT_BYTES) : 0); } /** * @see LogWritable#writeToLog */ public void writeToLog( ByteBuffer buf){ LogUtils.writeInt(buf,size); if (data != null) { LogUtils.writeInt(buf,data.length); for (int i=0; i < data.length; i+=1) { LogUtils.writeShort(buf,data[i]); } } else { LogUtils.writeInt(buf,0); } } /** * @see LogReadable#readFromLog */ public void readFromLog( ByteBuffer buf, byte entryTypeVersion){ size=LogUtils.readInt(buf); int len=LogUtils.readInt(buf); if (len > 0) { data=new short[len]; for (int i=0; i < len; i+=1) { data[i]=LogUtils.readShort(buf); } } } /** * @see LogReadable#dumpLog */ public void dumpLog( StringBuffer buf, boolean verbose){ if (size > 0) { Iterator i=iterator(); buf.append("<offsets size=\""); buf.append(size); buf.append("\">"); while (i.hasNext()) { buf.append("0x"); buf.append(Long.toHexString(i.next())); buf.append(' '); } buf.append("</offsets>"); } else { buf.append("<offsets size=\"0\"/>"); } } /** * Never called. * @see LogReadable#getTransactionId */ public long getTransactionId(){ return -1; } /** * Never called. * @see LogReadable#logEntryIsTransactional */ public boolean logEntryIsTransactional(){ return false; } public String toString(){ StringBuffer buf=new StringBuffer(); dumpLog(buf,true); return buf.toString(); } }