package com.sleepycat.je.log;
import java.nio.ByteBuffer;
import java.sql.Timestamp;
import javax.transaction.xa.Xid;
import de.ovgu.cide.jakutil.*;
/**
* This class holds convenience methods for marshalling internal JE data to and
* from the log.
*/
public class LogUtils {
public static final int SHORT_BYTES=2;
public static final int INT_BYTES=4;
public static final int LONG_BYTES=8;
public static final int UNSIGNED_INT_BYTES=4;
private static final boolean DEBUG=false;
public static final byte[] ZERO_LENGTH_BYTE_ARRAY=new byte[0];
/**
* Marshall a long into the next 4 bytes in this buffer. Necessary when the
* long is used to hold an unsigned int.
*/
public static void writeUnsignedInt( ByteBuffer buf, long value){
buf.put((byte)(value >>> 0));
buf.put((byte)(value >>> 8));
buf.put((byte)(value >>> 16));
buf.put((byte)(value >>> 24));
}
/**
* Unmarshall the next four bytes which hold an unsigned int into a long.
*/
public static long getUnsignedInt( ByteBuffer buf){
long ret=(buf.get() & 0xFFL) << 0;
ret+=(buf.get() & 0xFFL) << 8;
ret+=(buf.get() & 0xFFL) << 16;
ret+=(buf.get() & 0xFFL) << 24;
return ret;
}
/**
* Write a short into the log.
*/
public static void writeShort( ByteBuffer logBuf, short i){
byte b=(byte)((i >> 0) & 0xff);
logBuf.put(b);
b=(byte)((i >> 8) & 0xff);
logBuf.put(b);
}
/**
* Read a short from the log.
*/
public static short readShort( ByteBuffer logBuf){
return (short)(((logBuf.get() & 0xFF) << 0) + ((logBuf.get() & 0xFF) << 8));
}
/**
* Write an int into the log.
*/
public static void writeInt( ByteBuffer logBuf, int i){
byte b=(byte)((i >> 0) & 0xff);
logBuf.put(b);
b=(byte)((i >> 8) & 0xff);
logBuf.put(b);
b=(byte)((i >> 16) & 0xff);
logBuf.put(b);
b=(byte)((i >> 24) & 0xff);
logBuf.put(b);
}
/**
* Read a int from the log.
*/
public static int readInt( ByteBuffer logBuf){
int ret=(logBuf.get() & 0xFF) << 0;
ret+=(logBuf.get() & 0xFF) << 8;
ret+=(logBuf.get() & 0xFF) << 16;
ret+=(logBuf.get() & 0xFF) << 24;
return ret;
}
/**
* @return log storage size for a byteArray.
*/
public static int getIntLogSize(){
return INT_BYTES;
}
/**
* Write an int into the log in MSB order. Used for ordered keys.
*/
public static void writeIntMSB( ByteBuffer logBuf, int i){
byte b=(byte)((i >> 24) & 0xff);
logBuf.put(b);
b=(byte)((i >> 16) & 0xff);
logBuf.put(b);
b=(byte)((i >> 8) & 0xff);
logBuf.put(b);
b=(byte)((i >> 0) & 0xff);
logBuf.put(b);
}
/**
* Read a int from the log in MSB order. Used for ordered keys.
*/
public static int readIntMSB( ByteBuffer logBuf){
int ret=(logBuf.get() & 0xFF) << 24;
ret+=(logBuf.get() & 0xFF) << 16;
ret+=(logBuf.get() & 0xFF) << 8;
ret+=(logBuf.get() & 0xFF) << 0;
return ret;
}
/**
* Write a long into the log.
*/
public static void writeLong( ByteBuffer logBuf, long l){
byte b=(byte)(l >>> 0);
logBuf.put(b);
b=(byte)(l >>> 8);
logBuf.put(b);
b=(byte)(l >>> 16);
logBuf.put(b);
b=(byte)(l >>> 24);
logBuf.put(b);
b=(byte)(l >>> 32);
logBuf.put(b);
b=(byte)(l >>> 40);
logBuf.put(b);
b=(byte)(l >>> 48);
logBuf.put(b);
b=(byte)(l >>> 56);
logBuf.put(b);
}
/**
* Read a long from the log.
*/
public static long readLong( ByteBuffer logBuf){
long ret=(logBuf.get() & 0xFFL) << 0;
ret+=(logBuf.get() & 0xFFL) << 8;
ret+=(logBuf.get() & 0xFFL) << 16;
ret+=(logBuf.get() & 0xFFL) << 24;
ret+=(logBuf.get() & 0xFFL) << 32;
ret+=(logBuf.get() & 0xFFL) << 40;
ret+=(logBuf.get() & 0xFFL) << 48;
ret+=(logBuf.get() & 0xFFL) << 56;
return ret;
}
/**
* @return log storage size for a byteArray.
*/
public static int getLongLogSize(){
return LONG_BYTES;
}
/**
* Write a byte array into the log. The size is stored first as an integer.
*/
public static void writeByteArray( ByteBuffer logBuf, byte[] b){
writeInt(logBuf,b.length);
logBuf.put(b);
}
/**
* Read a byte array from the log. The size is stored first as an integer.
*/
public static byte[] readByteArray( ByteBuffer logBuf){
int size=readInt(logBuf);
if (DEBUG) {
System.out.println("pos = " + logBuf.position() + " byteArray is "+ size+ " on read");
}
if (size == 0) {
return ZERO_LENGTH_BYTE_ARRAY;
}
byte[] b=new byte[size];
logBuf.get(b);
return b;
}
/**
* @return log storage size for a byteArray
*/
public static int getByteArrayLogSize( byte[] b){
return INT_BYTES + b.length;
}
/**
* Write a string into the log. The size is stored first as an integer.
*/
public static void writeString( ByteBuffer logBuf, String stringVal){
writeByteArray(logBuf,stringVal.getBytes());
}
/**
* Read a string from the log. The size is stored first as an integer.
*/
public static String readString( ByteBuffer logBuf){
return new String(readByteArray(logBuf));
}
/**
* @return log storage size for a string
*/
public static int getStringLogSize( String s){
return INT_BYTES + s.length();
}
/**
* Write a timestamp into the log.
*/
public static void writeTimestamp( ByteBuffer logBuf, Timestamp time){
writeLong(logBuf,time.getTime());
}
/**
* Read a timestamp from the log.
*/
public static Timestamp readTimestamp( ByteBuffer logBuf){
long millis=readLong(logBuf);
return new Timestamp(millis);
}
/**
* @return log storage size for a timestamp
*/
public static int getTimestampLogSize(){
return LONG_BYTES;
}
/**
* Write a boolean into the log.
*/
public static void writeBoolean( ByteBuffer logBuf, boolean bool){
byte val=bool ? (byte)1 : (byte)0;
logBuf.put(val);
}
/**
* Read a boolean from the log.
*/
public static boolean readBoolean( ByteBuffer logBuf){
byte val=logBuf.get();
return (val == (byte)1) ? true : false;
}
/**
* @return log storage size for a boolean.
*/
public static int getBooleanLogSize(){
return 1;
}
public static boolean dumpBoolean( ByteBuffer itemBuffer, StringBuffer sb, String tag){
sb.append("<");
sb.append(tag);
sb.append(" exists = \"");
boolean exists=readBoolean(itemBuffer);
sb.append(exists);
if (exists) {
sb.append("\">");
}
else {
sb.append("\"/>");
}
return exists;
}
/**
* The byte[]'s in Xid's are known to be 255 or less in length. So instead
* of using read/writeByteArray(), we can save 6 bytes per record by making
* the byte[] length be 1 byte instead of 4.
*/
public static int getXidSize( Xid xid){
byte[] gid=xid.getGlobalTransactionId();
byte[] bqual=xid.getBranchQualifier();
return INT_BYTES + 1 + 1+ (gid == null ? 0 : gid.length)+ (bqual == null ? 0 : bqual.length);
}
public static void writeXid( ByteBuffer logBuf, Xid xid){
byte[] gid=xid.getGlobalTransactionId();
byte[] bqual=xid.getBranchQualifier();
writeInt(logBuf,xid.getFormatId());
if (gid == null) {
logBuf.put((byte)-1);
}
else {
logBuf.put((byte)(gid.length));
logBuf.put(gid);
}
if (bqual == null) {
logBuf.put((byte)-1);
}
else {
logBuf.put((byte)(bqual.length));
logBuf.put(bqual);
}
}
public static Xid readXid( ByteBuffer logBuf){
int formatId=readInt(logBuf);
int gidLen=logBuf.get();
byte[] gid=null;
if (gidLen >= 0) {
gid=new byte[gidLen];
logBuf.get(gid);
}
int bqualLen=logBuf.get();
byte[] bqual=null;
if (bqualLen >= 0) {
bqual=new byte[bqualLen];
logBuf.get(bqual);
}
return new XidImpl(formatId,gid,bqual);
}
public static class XidImpl implements Xid {
private int formatId;
private byte[] gid;
private byte[] bqual;
public XidImpl( int formatId, byte[] gid, byte[] bqual){
this.formatId=formatId;
this.gid=gid;
this.bqual=bqual;
}
public int getFormatId(){
return formatId;
}
public byte[] getGlobalTransactionId(){
return gid;
}
public byte[] getBranchQualifier(){
return bqual;
}
public boolean equals( Object o){
if (!(o instanceof XidImpl)) {
return false;
}
XidImpl xid=(XidImpl)o;
if (xid.getFormatId() != formatId) {
return false;
}
if (compareByteArrays(xid.getGlobalTransactionId(),gid) && compareByteArrays(xid.getBranchQualifier(),bqual)) {
return true;
}
return false;
}
public int hashCode(){
int code=formatId;
if (gid != null) {
for (int i=0; i < gid.length; i++) {
code+=gid[i];
}
}
if (bqual != null) {
for (int i=0; i < bqual.length; i++) {
code+=bqual[i];
}
}
return code;
}
private boolean compareByteArrays( byte[] b1, byte[] b2){
if (b1 == null || b2 == null) {
return b1 == b2;
}
if (b1.length != b2.length) {
return false;
}
for (int i=0; i < b1.length; i++) {
if (b1[i] != b2[i]) {
return false;
}
}
return true;
}
public String toString(){
StringBuffer sb=new StringBuffer();
sb.append("<Xid formatId=\"").append(formatId);
sb.append("\" gTxnId=\"");
if (gid == null) {
sb.append("null");
}
else {
sb.append(new String(gid));
}
sb.append("\" bqual=\"");
if (bqual == null) {
sb.append("null");
}
else {
sb.append(new String(bqual));
}
sb.append("\"/>");
return sb.toString();
}
}
}