001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LogUtils.java,v 1.50.2.3 2008/01/07 15:14:13 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.nio.ByteBuffer;
012: import java.sql.Timestamp;
013:
014: import javax.transaction.xa.Xid;
015:
016: /**
017: * This class holds convenience methods for marshalling internal JE data to and
018: * from the log.
019: */
020: public class LogUtils {
021: /* Storage sizes for int, long in log. */
022: public static final int SHORT_BYTES = 2;
023: public static final int INT_BYTES = 4;
024: public static final int LONG_BYTES = 8;
025: public static final int UNSIGNED_INT_BYTES = 4;
026:
027: private static final boolean DEBUG = false;
028:
029: /*
030: * We can return the same byte[] for 0 length arrays.
031: */
032: public static final byte[] ZERO_LENGTH_BYTE_ARRAY = new byte[0];
033:
034: /**
035: * Marshall a long into the next 4 bytes in this buffer. Necessary when the
036: * long is used to hold an unsigned int.
037: */
038: public static void writeUnsignedInt(ByteBuffer buf, long value) {
039: buf.put((byte) (value >>> 0));
040: buf.put((byte) (value >>> 8));
041: buf.put((byte) (value >>> 16));
042: buf.put((byte) (value >>> 24));
043: }
044:
045: /**
046: * Unmarshall the next four bytes which hold an unsigned int into a long.
047: */
048: public static long getUnsignedInt(ByteBuffer buf) {
049: long ret = (buf.get() & 0xFFL) << 0;
050: ret += (buf.get() & 0xFFL) << 8;
051: ret += (buf.get() & 0xFFL) << 16;
052: ret += (buf.get() & 0xFFL) << 24;
053: return ret;
054: }
055:
056: /*
057: * Marshall objects.
058: */
059:
060: /**
061: * Write a short into the log.
062: */
063: public static void writeShort(ByteBuffer logBuf, short i) {
064: byte b = (byte) ((i >> 0) & 0xff);
065: logBuf.put(b);
066: b = (byte) ((i >> 8) & 0xff);
067: logBuf.put(b);
068: }
069:
070: /**
071: * Read a short from the log.
072: */
073: public static short readShort(ByteBuffer logBuf) {
074: return (short) (((logBuf.get() & 0xFF) << 0) + ((logBuf.get() & 0xFF) << 8));
075: }
076:
077: /**
078: * Write an int into the log.
079: */
080: public static void writeInt(ByteBuffer logBuf, int i) {
081: byte b = (byte) ((i >> 0) & 0xff);
082: logBuf.put(b);
083: b = (byte) ((i >> 8) & 0xff);
084: logBuf.put(b);
085: b = (byte) ((i >> 16) & 0xff);
086: logBuf.put(b);
087: b = (byte) ((i >> 24) & 0xff);
088: logBuf.put(b);
089: }
090:
091: /**
092: * Read a int from the log.
093: */
094: public static int readInt(ByteBuffer logBuf) {
095: int ret = (logBuf.get() & 0xFF) << 0;
096: ret += (logBuf.get() & 0xFF) << 8;
097: ret += (logBuf.get() & 0xFF) << 16;
098: ret += (logBuf.get() & 0xFF) << 24;
099: return ret;
100: }
101:
102: /**
103: * @return log storage size for a byteArray.
104: */
105: public static int getIntLogSize() {
106: return INT_BYTES;
107: }
108:
109: /**
110: * Write an int into the log in MSB order. Used for ordered keys.
111: */
112: public static void writeIntMSB(ByteBuffer logBuf, int i) {
113: byte b = (byte) ((i >> 24) & 0xff);
114: logBuf.put(b);
115: b = (byte) ((i >> 16) & 0xff);
116: logBuf.put(b);
117: b = (byte) ((i >> 8) & 0xff);
118: logBuf.put(b);
119: b = (byte) ((i >> 0) & 0xff);
120: logBuf.put(b);
121: }
122:
123: /**
124: * Read a int from the log in MSB order. Used for ordered keys.
125: */
126: public static int readIntMSB(ByteBuffer logBuf) {
127: int ret = (logBuf.get() & 0xFF) << 24;
128: ret += (logBuf.get() & 0xFF) << 16;
129: ret += (logBuf.get() & 0xFF) << 8;
130: ret += (logBuf.get() & 0xFF) << 0;
131: return ret;
132: }
133:
134: /**
135: * Write a long into the log.
136: */
137: public static void writeLong(ByteBuffer logBuf, long l) {
138: byte b = (byte) (l >>> 0);
139: logBuf.put(b);
140: b = (byte) (l >>> 8);
141: logBuf.put(b);
142: b = (byte) (l >>> 16);
143: logBuf.put(b);
144: b = (byte) (l >>> 24);
145: logBuf.put(b);
146: b = (byte) (l >>> 32);
147: logBuf.put(b);
148: b = (byte) (l >>> 40);
149: logBuf.put(b);
150: b = (byte) (l >>> 48);
151: logBuf.put(b);
152: b = (byte) (l >>> 56);
153: logBuf.put(b);
154: }
155:
156: /**
157: * Read a long from the log.
158: */
159: public static long readLong(ByteBuffer logBuf) {
160: long ret = (logBuf.get() & 0xFFL) << 0;
161: ret += (logBuf.get() & 0xFFL) << 8;
162: ret += (logBuf.get() & 0xFFL) << 16;
163: ret += (logBuf.get() & 0xFFL) << 24;
164: ret += (logBuf.get() & 0xFFL) << 32;
165: ret += (logBuf.get() & 0xFFL) << 40;
166: ret += (logBuf.get() & 0xFFL) << 48;
167: ret += (logBuf.get() & 0xFFL) << 56;
168: return ret;
169: }
170:
171: /**
172: * @return log storage size for a byteArray.
173: */
174: public static int getLongLogSize() {
175: return LONG_BYTES;
176: }
177:
178: /**
179: * Write a byte array into the log. The size is stored first as an integer.
180: */
181: public static void writeByteArray(ByteBuffer logBuf, byte[] b) {
182:
183: /* Write the length. */
184: writeInt(logBuf, b.length);
185:
186: /* Add the data itself. */
187: logBuf.put(b); // data
188: }
189:
190: /**
191: * Read a byte array from the log. The size is stored first as an integer.
192: */
193: public static byte[] readByteArray(ByteBuffer logBuf) {
194: int size = readInt(logBuf); // how long is it?
195: if (DEBUG) {
196: System.out.println("pos = " + logBuf.position()
197: + " byteArray is " + size + " on read");
198: }
199: if (size == 0) {
200: return ZERO_LENGTH_BYTE_ARRAY;
201: }
202: byte[] b = new byte[size];
203: logBuf.get(b); // read it out
204: return b;
205: }
206:
207: /**
208: * @return log storage size for a byteArray
209: */
210: public static int getByteArrayLogSize(byte[] b) {
211: return INT_BYTES + b.length;
212: }
213:
214: /**
215: * writeString and readString should both use something other than the
216: * default character encoding (e.g. UTF-8). But since switching now might
217: * cause incompatibilities with existing databases, we need to do something
218: * more complicated than just add "UTF-8" to the getBytes and "new
219: * String()" calls below. User-defined character strings are encoded using
220: * these methods when XA is used. See [#15293].
221: */
222:
223: /**
224: * Write a string into the log. The size is stored first as an integer.
225: */
226: public static void writeString(ByteBuffer logBuf, String stringVal) {
227: writeByteArray(logBuf, stringVal.getBytes());
228: }
229:
230: /**
231: * Read a string from the log. The size is stored first as an integer.
232: */
233: public static String readString(ByteBuffer logBuf) {
234: return new String(readByteArray(logBuf));
235: }
236:
237: /**
238: * @return log storage size for a string
239: */
240: public static int getStringLogSize(String s) {
241: return INT_BYTES + s.getBytes().length;
242: }
243:
244: /**
245: * Write a timestamp into the log.
246: */
247: public static void writeTimestamp(ByteBuffer logBuf, Timestamp time) {
248: writeLong(logBuf, time.getTime());
249: }
250:
251: /**
252: * Read a timestamp from the log.
253: */
254: public static Timestamp readTimestamp(ByteBuffer logBuf) {
255: long millis = readLong(logBuf);
256: return new Timestamp(millis);
257: }
258:
259: /**
260: * @return log storage size for a timestamp
261: */
262: public static int getTimestampLogSize() {
263: return LONG_BYTES;
264: }
265:
266: /**
267: * Write a boolean into the log.
268: */
269: public static void writeBoolean(ByteBuffer logBuf, boolean bool) {
270: byte val = bool ? (byte) 1 : (byte) 0;
271: logBuf.put(val);
272: }
273:
274: /**
275: * Read a boolean from the log.
276: */
277: public static boolean readBoolean(ByteBuffer logBuf) {
278: byte val = logBuf.get();
279: return (val == (byte) 1) ? true : false;
280: }
281:
282: /**
283: * @return log storage size for a boolean.
284: */
285: public static int getBooleanLogSize() {
286: return 1;
287: }
288:
289: /*
290: * Dumping support.
291: */
292: public static boolean dumpBoolean(ByteBuffer itemBuffer,
293: StringBuffer sb, String tag) {
294: sb.append("<");
295: sb.append(tag);
296: sb.append(" exists = \"");
297: boolean exists = readBoolean(itemBuffer);
298: sb.append(exists);
299: if (exists) {
300: sb.append("\">");
301: } else {
302: /* Close off the tag, we're done. */
303: sb.append("\"/>");
304: }
305: return exists;
306: }
307:
308: /**
309: * The byte[]'s in Xid's are known to be 255 or less in length. So instead
310: * of using read/writeByteArray(), we can save 6 bytes per record by making
311: * the byte[] length be 1 byte instead of 4.
312: */
313: public static int getXidSize(Xid xid) {
314: byte[] gid = xid.getGlobalTransactionId();
315: byte[] bqual = xid.getBranchQualifier();
316: return INT_BYTES + // FormatId
317: 1 + // gxid length byte
318: 1 + // bqual length byte
319: (gid == null ? 0 : gid.length) + // gid bytes
320: (bqual == null ? 0 : bqual.length); // bqual bytes
321: }
322:
323: /*
324: * Xid.gid[] and bqual[] can't be longer than 64 bytes so we can get away
325: * with writing the length in one byte, rather than 4.
326: */
327: public static void writeXid(ByteBuffer logBuf, Xid xid) {
328: byte[] gid = xid.getGlobalTransactionId();
329: byte[] bqual = xid.getBranchQualifier();
330:
331: writeInt(logBuf, xid.getFormatId());
332:
333: if (gid == null) {
334: logBuf.put((byte) -1);
335: } else {
336: logBuf.put((byte) (gid.length));
337: logBuf.put(gid);
338: }
339:
340: if (bqual == null) {
341: logBuf.put((byte) -1);
342: } else {
343: logBuf.put((byte) (bqual.length));
344: logBuf.put(bqual);
345: }
346: }
347:
348: /*
349: * Xid.gid[] and bqual[] can't be longer than 64 bytes so we can get away
350: * with writing the length in one byte, rather than 4.
351: */
352: public static Xid readXid(ByteBuffer logBuf) {
353: int formatId = readInt(logBuf);
354:
355: int gidLen = logBuf.get();
356: byte[] gid = null;
357: if (gidLen >= 0) {
358: gid = new byte[gidLen];
359: logBuf.get(gid);
360: }
361:
362: int bqualLen = logBuf.get();
363: byte[] bqual = null;
364: if (bqualLen >= 0) {
365: bqual = new byte[bqualLen];
366: logBuf.get(bqual);
367: }
368:
369: return new XidImpl(formatId, gid, bqual);
370: }
371:
372: public static class XidImpl implements Xid {
373: private int formatId;
374: private byte[] gid;
375: private byte[] bqual;
376:
377: /* public for unit tests. */
378: public XidImpl(int formatId, byte[] gid, byte[] bqual) {
379: this .formatId = formatId;
380: this .gid = gid;
381: this .bqual = bqual;
382: }
383:
384: public int getFormatId() {
385: return formatId;
386: }
387:
388: public byte[] getGlobalTransactionId() {
389: return gid;
390: }
391:
392: public byte[] getBranchQualifier() {
393: return bqual;
394: }
395:
396: public boolean equals(Object o) {
397: if (!(o instanceof XidImpl)) {
398: return false;
399: }
400:
401: XidImpl xid = (XidImpl) o;
402: if (xid.getFormatId() != formatId) {
403: return false;
404: }
405: if (compareByteArrays(xid.getGlobalTransactionId(), gid)
406: && compareByteArrays(xid.getBranchQualifier(),
407: bqual)) {
408: return true;
409: }
410:
411: return false;
412: }
413:
414: public int hashCode() {
415: int code = formatId;
416: if (gid != null) {
417: for (int i = 0; i < gid.length; i++) {
418: code += gid[i];
419: }
420: }
421: if (bqual != null) {
422: for (int i = 0; i < bqual.length; i++) {
423: code += bqual[i];
424: }
425: }
426: return code;
427: }
428:
429: private boolean compareByteArrays(byte[] b1, byte[] b2) {
430: if (b1 == null || b2 == null) {
431: return b1 == b2;
432: }
433:
434: if (b1.length != b2.length) {
435: return false;
436: }
437:
438: for (int i = 0; i < b1.length; i++) {
439: if (b1[i] != b2[i]) {
440: return false;
441: }
442: }
443:
444: return true;
445: }
446:
447: public String toString() {
448: StringBuffer sb = new StringBuffer();
449: sb.append("<Xid formatId=\"").append(formatId);
450: sb.append("\" gTxnId=\"");
451: if (gid == null) {
452: sb.append("null");
453: } else {
454: sb.append(new String(gid));
455: }
456: sb.append("\" bqual=\"");
457: if (bqual == null) {
458: sb.append("null");
459: } else {
460: sb.append(new String(bqual));
461: }
462: sb.append("\"/>");
463: return sb.toString();
464: }
465: }
466: }
|