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