1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.log;
9 
10 import java.nio.ByteBuffer;
11 import java.nio.charset.Charset;
12 
13 import javax.transaction.xa.Xid;
14 
15 import com.sleepycat.je.utilint.Timestamp;
16 import com.sleepycat.util.PackedInteger;
17 import com.sleepycat.utilint.StringUtils;
18 
19 /**
20  * This class holds convenience methods for marshalling internal JE data to and
21  * from the log.
22  */
23 public class LogUtils {
24     /* Storage sizes for int, long in log. */
25     public static final int SHORT_BYTES = 2;
26     public static final int INT_BYTES = 4;
27     public static final int LONG_BYTES = 8;
28     public static final int UNSIGNED_INT_BYTES = 4;
29 
30     private static final boolean DEBUG = false;
31 
32     /*
33      * We can return the same byte[] for 0 length arrays.
34      */
35     public static final byte[] ZERO_LENGTH_BYTE_ARRAY = new byte[0];
36 
37     /*
38      * The je.logCharset system property can be specified when running
39      * DbPrintLog to work around the charset issue in JE 5.0 and earlier (see
40      * [#15296] below).  For example, because of this issue, on a z/OS system
41      * the trace messages and other internal strings (such as the checkpoint
42      * invoker) are stored in the log in EBCDIC encoding.  The following system
43      * property allows such strings to be viewed correctly in the DbPrintLog
44      * output:
45      *      -Dje.logCharset=IBM1047
46      *
47      * WARNING: Do not specify this property when running an application that
48      * writes to the log.  It is only for reading a log (such as with
49      * DbPrintLog) that was written with a non-ANSI-based default charset.
50      */
51     private static Charset logCharset = null;
52     static {
53         final String charsetName = System.getProperty("je.logCharset");
54         if (charsetName != null && charsetName.length() > 0) {
55             try {
56                 logCharset = Charset.forName(charsetName);
57             } catch (RuntimeException e) {
58                 e.printStackTrace();
59             }
60         }
61     }
62 
63     /**
64      * Marshall a long into the next 4 bytes in this buffer. Necessary when the
65      * long is used to hold an unsigned int.
66      */
writeUnsignedInt(ByteBuffer buf, long value)67     public static void writeUnsignedInt(ByteBuffer buf, long value) {
68         buf.put((byte) (value >>> 0));
69         buf.put((byte) (value >>> 8));
70         buf.put((byte) (value >>> 16));
71         buf.put((byte) (value >>> 24));
72     }
73 
74     /**
75      * Unmarshall the next four bytes which hold an unsigned int into a long.
76      */
readUnsignedInt(ByteBuffer buf)77     public static long readUnsignedInt(ByteBuffer buf) {
78         long ret = (buf.get() & 0xFFL) << 0;
79         ret += (buf.get() & 0xFFL) << 8;
80         ret += (buf.get() & 0xFFL) << 16;
81         ret += (buf.get() & 0xFFL) << 24;
82         return ret;
83     }
84 
85     /*
86      * Marshall objects.
87      */
88 
89     /**
90      * Write a short into the log.
91      */
writeShort(ByteBuffer logBuf, short i)92     public static void writeShort(ByteBuffer logBuf, short i) {
93         byte b = (byte) ((i >> 0) & 0xff);
94         logBuf.put(b);
95         b = (byte) ((i >> 8) & 0xff);
96         logBuf.put(b);
97     }
98 
99     /**
100      * Read a short from the log.
101      */
readShort(ByteBuffer logBuf)102     public static short readShort(ByteBuffer logBuf) {
103         return (short) (((logBuf.get() & 0xFF) << 0) +
104                         ((logBuf.get() & 0xFF) << 8));
105     }
106 
107     /**
108      * Read an int from the log in either packed or unpacked format.
109      */
readInt(ByteBuffer logBuf, boolean unpacked)110     public static int readInt(ByteBuffer logBuf, boolean unpacked) {
111         if (unpacked) {
112             return readInt(logBuf);
113         } else {
114             return readPackedInt(logBuf);
115         }
116     }
117 
118     /**
119      * Write an int into the log.
120      */
writeInt(ByteBuffer logBuf, int i)121     public static void writeInt(ByteBuffer logBuf, int i) {
122         byte b = (byte) ((i >> 0) & 0xff);
123         logBuf.put(b);
124         b = (byte) ((i >> 8) & 0xff);
125         logBuf.put(b);
126         b = (byte) ((i >> 16) & 0xff);
127         logBuf.put(b);
128         b = (byte) ((i >> 24) & 0xff);
129         logBuf.put(b);
130     }
131 
132     /**
133      * Read a int from the log.
134      */
readInt(ByteBuffer logBuf)135     public static int readInt(ByteBuffer logBuf) {
136         int ret = (logBuf.get() & 0xFF) << 0;
137         ret += (logBuf.get() & 0xFF) << 8;
138         ret += (logBuf.get() & 0xFF) << 16;
139         ret += (logBuf.get() & 0xFF) << 24;
140         return ret;
141     }
142 
143     /**
144      * @return log storage size for an int.
145      */
getIntLogSize()146     public static int getIntLogSize() {
147         return INT_BYTES;
148     }
149 
150     /**
151      * Write a packed int into the log.
152      */
writePackedInt(ByteBuffer logBuf, int i)153     public static void writePackedInt(ByteBuffer logBuf, int i) {
154         int off = logBuf.arrayOffset();
155         int newPos =
156             PackedInteger.writeInt(logBuf.array(),
157                                    logBuf.position() + off, i);
158         logBuf.position(newPos - off);
159     }
160 
161     /**
162      * Read a packed int from the log.
163      */
readPackedInt(ByteBuffer logBuf)164     public static int readPackedInt(ByteBuffer logBuf) {
165         byte a[] = logBuf.array();
166         int oldPos = logBuf.position();
167         int off = logBuf.arrayOffset() + oldPos;
168         int len = PackedInteger.getReadIntLength(a, off);
169         int val = PackedInteger.readInt(a, off);
170         logBuf.position(oldPos + len);
171         return val;
172     }
173 
174     /**
175      * @return log storage size for a packed int.
176      */
getPackedIntLogSize(int i)177     public static int getPackedIntLogSize(int i) {
178         return PackedInteger.getWriteIntLength(i);
179     }
180 
181     /**
182      * Write an int into the log in MSB order.  Used for ordered keys.
183      */
writeIntMSB(ByteBuffer logBuf, int i)184     public static void writeIntMSB(ByteBuffer logBuf, int i) {
185         byte b = (byte) ((i >> 24) & 0xff);
186         logBuf.put(b);
187         b = (byte) ((i >> 16) & 0xff);
188         logBuf.put(b);
189         b = (byte) ((i >> 8) & 0xff);
190         logBuf.put(b);
191         b = (byte) ((i >> 0) & 0xff);
192         logBuf.put(b);
193     }
194 
195     /**
196      * Read a int from the log in MSB order.  Used for ordered keys.
197      */
readIntMSB(ByteBuffer logBuf)198     public static int readIntMSB(ByteBuffer logBuf) {
199         int ret = (logBuf.get() & 0xFF) << 24;
200         ret += (logBuf.get() & 0xFF) << 16;
201         ret += (logBuf.get() & 0xFF) << 8;
202         ret += (logBuf.get() & 0xFF) << 0;
203         return ret;
204     }
205 
206     /**
207      * Write a long into the log.
208      */
writeLong(ByteBuffer logBuf, long l)209     public static void writeLong(ByteBuffer logBuf, long l) {
210         byte b =(byte) (l >>> 0);
211         logBuf.put(b);
212         b =(byte) (l >>> 8);
213         logBuf.put(b);
214         b =(byte) (l >>> 16);
215         logBuf.put(b);
216         b =(byte) (l >>> 24);
217         logBuf.put(b);
218         b =(byte) (l >>> 32);
219         logBuf.put(b);
220         b =(byte) (l >>> 40);
221         logBuf.put(b);
222         b =(byte) (l >>> 48);
223         logBuf.put(b);
224         b =(byte) (l >>> 56);
225         logBuf.put(b);
226     }
227 
228     /**
229      * Read an int from the log in either packed or unpacked format.
230      */
readLong(ByteBuffer logBuf, boolean unpacked)231     public static long readLong(ByteBuffer logBuf, boolean unpacked) {
232         if (unpacked) {
233             return readLong(logBuf);
234         } else {
235             return readPackedLong(logBuf);
236         }
237     }
238 
239     /**
240      * Read a long from the log.
241      */
readLong(ByteBuffer logBuf)242     public static long readLong(ByteBuffer logBuf) {
243         long ret = (logBuf.get() & 0xFFL) << 0;
244         ret += (logBuf.get() & 0xFFL) << 8;
245         ret += (logBuf.get() & 0xFFL) << 16;
246         ret += (logBuf.get() & 0xFFL) << 24;
247         ret += (logBuf.get() & 0xFFL) << 32;
248         ret += (logBuf.get() & 0xFFL) << 40;
249         ret += (logBuf.get() & 0xFFL) << 48;
250         ret += (logBuf.get() & 0xFFL) << 56;
251         return ret;
252     }
253 
254     /**
255      * @return log storage size for a long.
256      */
getLongLogSize()257     public static int getLongLogSize() {
258         return LONG_BYTES;
259     }
260 
261     /**
262      * Write a packed long into the log.
263      */
writePackedLong(ByteBuffer logBuf, long l)264     public static void writePackedLong(ByteBuffer logBuf, long l) {
265         int off = logBuf.arrayOffset();
266         int newPos =
267             PackedInteger.writeLong(logBuf.array(),
268                                     logBuf.position() + off, l);
269         logBuf.position(newPos - off);
270     }
271 
272     /**
273      * Read a packed long from the log.
274      */
readPackedLong(ByteBuffer logBuf)275     public static long readPackedLong(ByteBuffer logBuf) {
276         byte a[] = logBuf.array();
277         int oldPos = logBuf.position();
278         int off = logBuf.arrayOffset() + oldPos;
279         int len = PackedInteger.getReadLongLength(a, off);
280         long val = PackedInteger.readLong(a, off);
281         logBuf.position(oldPos + len);
282         return val;
283     }
284 
285     /**
286      * @return log storage size for a packed long.
287      */
getPackedLongLogSize(long l)288     public static int getPackedLongLogSize(long l) {
289         return PackedInteger.getWriteLongLength(l);
290     }
291 
292     /**
293      * Write a byte array into the log. The size is stored first as an integer.
294      */
writeByteArray(ByteBuffer logBuf, byte[] b)295     public static void writeByteArray(ByteBuffer logBuf, byte[] b) {
296 
297         if (b == null) {
298             writePackedInt(logBuf, -1);
299             return;
300         }
301 
302         /* Write the length. */
303         writePackedInt(logBuf, b.length);
304 
305         /* Add the data itself. */
306         logBuf.put(b);                     // data
307     }
308 
309     /**
310      * Read a byte array from the log. The size is stored first as an integer.
311      */
readByteArray(ByteBuffer logBuf, boolean unpacked)312     public static byte[] readByteArray(ByteBuffer logBuf, boolean unpacked) {
313         int size = readInt(logBuf, unpacked);
314         if (DEBUG) {
315             System.out.println("pos = " + logBuf.position() +
316                                " byteArray is " + size + " on read");
317         }
318 
319         if (size < 0) {
320             return null;
321         }
322 
323         if (size == 0) {
324             return ZERO_LENGTH_BYTE_ARRAY;
325         }
326 
327         byte[] b = new byte[size];
328         logBuf.get(b);               // read it out
329         return b;
330     }
331 
332     /**
333      * @return log storage size for a byteArray
334      */
getByteArrayLogSize(byte[] b)335     public static int getByteArrayLogSize(byte[] b) {
336         if (b == null) {
337             return LogUtils.getPackedIntLogSize(-1);
338         } else {
339             int len = b.length;
340             return LogUtils.getPackedIntLogSize(len) + len;
341         }
342     }
343 
344     /**
345      * Write a byte array into the log. No size is stored.
346      */
writeBytesNoLength(ByteBuffer logBuf, byte[] b)347     public static void writeBytesNoLength(ByteBuffer logBuf, byte[] b) {
348 
349         /* Add the data itself. */
350         logBuf.put(b);
351     }
352 
353     /**
354      * Read a byte array from the log.  The size is not stored.
355      */
readBytesNoLength(ByteBuffer logBuf, int size)356     public static byte[] readBytesNoLength(ByteBuffer logBuf, int size) {
357         if (DEBUG) {
358             System.out.println("pos = " + logBuf.position() +
359                                " byteArray is " + size + " on read");
360         }
361 
362         if (size == 0) {
363             return ZERO_LENGTH_BYTE_ARRAY;
364         }
365 
366         byte[] b = new byte[size];
367         logBuf.get(b);               // read it out
368         return b;
369     }
370 
371     /**
372      * Write a string into the log. The size is stored first as an integer.
373      */
writeString(ByteBuffer logBuf, String stringVal)374     public static void writeString(ByteBuffer logBuf,
375                                    String stringVal) {
376         writeByteArray(logBuf, StringUtils.toUTF8(stringVal));
377     }
378 
379     /**
380      * Read a string from the log. The size is stored first as an integer.
381      */
readString(ByteBuffer logBuf, boolean unpacked, int entryVersion)382     public static String readString(ByteBuffer logBuf,
383                                     boolean unpacked,
384                                     int entryVersion) {
385         final byte[] bytes = readByteArray(logBuf, unpacked);
386 
387         /*
388          * Use logCharset only prior to version 9, since in version 9
389          * UTF8 is always used.  See logCharset for details.
390          */
391         if (entryVersion >= 9) {
392             return StringUtils.fromUTF8(bytes);
393         }
394         if (logCharset != null) {
395             return new String(bytes, logCharset);
396         }
397         return new String(bytes);
398     }
399 
400     /**
401      * @return log storage size for a string
402      */
getStringLogSize(String s)403     public static int getStringLogSize(String s) {
404         return getByteArrayLogSize(StringUtils.toUTF8(s));
405     }
406 
407     /**
408      * Write a timestamp into the log.
409      */
writeTimestamp(ByteBuffer logBuf, Timestamp time)410     public static void writeTimestamp(ByteBuffer logBuf, Timestamp time) {
411         writePackedLong(logBuf, time.getTime());
412     }
413 
414     /**
415      * Read a timestamp from the log.
416      */
readTimestamp(ByteBuffer logBuf, boolean unpacked)417     public static Timestamp readTimestamp(ByteBuffer logBuf,
418                                           boolean unpacked) {
419         long millis = readLong(logBuf, unpacked);
420         return new Timestamp(millis);
421     }
422 
423     /**
424      * @return log storage size for a timestamp
425      */
getTimestampLogSize(Timestamp time)426     public static int getTimestampLogSize(Timestamp time) {
427         return PackedInteger.getWriteLongLength(time.getTime());
428     }
429 
430     /**
431      * Write a boolean into the log.
432      */
writeBoolean(ByteBuffer logBuf, boolean bool)433     public static void writeBoolean(ByteBuffer logBuf, boolean bool) {
434         byte val = bool ? (byte) 1 : (byte) 0;
435         logBuf.put(val);
436     }
437 
438     /**
439      * Read a boolean from the log.
440      */
readBoolean(ByteBuffer logBuf)441     public static boolean readBoolean(ByteBuffer logBuf) {
442         byte val = logBuf.get();
443         return (val == (byte) 1) ? true : false;
444     }
445 
446     /**
447      * @return log storage size for a boolean.
448      */
getBooleanLogSize()449     public static int getBooleanLogSize() {
450         return 1;
451     }
452 
453     /*
454      * Dumping support.
455      */
dumpBoolean(ByteBuffer itemBuffer, StringBuilder sb, String tag)456     public static boolean dumpBoolean(ByteBuffer itemBuffer,
457                                       StringBuilder sb,
458                                       String tag) {
459         sb.append("<");
460         sb.append(tag);
461         sb.append(" exists = \"");
462         boolean exists = readBoolean(itemBuffer);
463         sb.append(exists);
464         if (exists) {
465             sb.append("\">");
466         } else {
467             /* Close off the tag, we're done. */
468             sb.append("\"/>");
469         }
470         return exists;
471     }
472 
473     /**
474      * The byte[]'s in Xid's are known to be 255 or less in length.  So instead
475      * of using read/writeByteArray(), we can save 6 bytes per record by making
476      * the byte[] length be 1 byte instead of 4.
477      */
getXidSize(Xid xid)478     public static int getXidSize(Xid xid) {
479         byte[] gid = xid.getGlobalTransactionId();
480         byte[] bqual = xid.getBranchQualifier();
481         return
482             INT_BYTES +                         // FormatId
483             1 +                                 // gxid length byte
484             1 +                                 // bqual length byte
485             (gid == null ? 0 : gid.length) +    // gid bytes
486             (bqual == null ? 0 : bqual.length); // bqual bytes
487     }
488 
489     /*
490      * Xid.gid[] and bqual[] can't be longer than 64 bytes so we can get away
491      * with writing the length in one byte, rather than 4.
492      */
writeXid(ByteBuffer logBuf, Xid xid)493     public static void writeXid(ByteBuffer logBuf, Xid xid) {
494         byte[] gid = xid.getGlobalTransactionId();
495         byte[] bqual = xid.getBranchQualifier();
496 
497         writeInt(logBuf, xid.getFormatId());
498 
499         if (gid == null) {
500             logBuf.put((byte) -1);
501         } else {
502             logBuf.put((byte) (gid.length));
503             logBuf.put(gid);
504         }
505 
506         if (bqual == null) {
507             logBuf.put((byte) -1);
508         } else {
509             logBuf.put((byte) (bqual.length));
510             logBuf.put(bqual);
511         }
512     }
513 
514     /*
515      * Xid.gid[] and bqual[] can't be longer than 64 bytes so we can get away
516      * with writing the length in one byte, rather than 4.
517      */
readXid(ByteBuffer logBuf)518     public static Xid readXid(ByteBuffer logBuf) {
519         int formatId = readInt(logBuf);
520 
521         int gidLen = logBuf.get();
522         byte[] gid = null;
523         if (gidLen >= 0) {
524             gid = new byte[gidLen];
525             logBuf.get(gid);
526         }
527 
528         int bqualLen = logBuf.get();
529         byte[] bqual = null;
530         if (bqualLen >= 0) {
531             bqual = new byte[bqualLen];
532             logBuf.get(bqual);
533         }
534 
535         return new XidImpl(formatId, gid, bqual);
536     }
537 
538     public static class XidImpl implements Xid {
539         private int formatId;
540         private byte[] gid;
541         private byte[] bqual;
542 
543         /* public for unit tests. */
XidImpl(int formatId, byte[] gid, byte[] bqual)544         public XidImpl(int formatId, byte[] gid, byte[] bqual) {
545             this.formatId = formatId;
546             this.gid = gid;
547             this.bqual = bqual;
548         }
549 
getFormatId()550         public int getFormatId() {
551             return formatId;
552         }
553 
getGlobalTransactionId()554         public byte[] getGlobalTransactionId() {
555             return gid;
556         }
557 
getBranchQualifier()558         public byte[] getBranchQualifier() {
559             return bqual;
560         }
561 
562         @Override
equals(Object o)563         public boolean equals(Object o) {
564             if (!(o instanceof XidImpl)) {
565                 return false;
566             }
567 
568             XidImpl xid = (XidImpl) o;
569             if (xid.getFormatId() != formatId) {
570                 return false;
571             }
572             if (compareByteArrays(xid.getGlobalTransactionId(), gid) &&
573                 compareByteArrays(xid.getBranchQualifier(), bqual)) {
574                 return true;
575             }
576 
577             return false;
578         }
579 
580         @Override
hashCode()581         public int hashCode() {
582             int code = formatId;
583             if (gid != null) {
584                 for (int i = 0; i < gid.length; i++) {
585                     code += gid[i];
586                 }
587             }
588             if (bqual != null) {
589                 for (int i = 0; i < bqual.length; i++) {
590                     code += bqual[i];
591                 }
592             }
593             return code;
594         }
595 
compareByteArrays(byte[] b1, byte[] b2)596         private boolean compareByteArrays(byte[] b1, byte[] b2) {
597             if (b1 == null ||
598                 b2 == null) {
599                 return b1 == b2;
600             }
601 
602             if (b1.length != b2.length) {
603                 return false;
604             }
605 
606             for (int i = 0; i < b1.length; i++) {
607                 if (b1[i] != b2[i]) {
608                     return false;
609                 }
610             }
611 
612             return true;
613         }
614 
615         @Override
toString()616         public String toString() {
617             StringBuilder sb = new StringBuilder();
618             sb.append("<Xid formatId=\"").append(formatId);
619             sb.append("\" gTxnId=\"");
620             if (gid == null) {
621                 sb.append("null");
622             } else {
623                 sb.append(new String(gid));
624             }
625             sb.append("\" bqual=\"");
626             if (bqual == null) {
627                 sb.append("null");
628             } else {
629                 sb.append(new String(bqual));
630             }
631             sb.append("\"/>");
632             return sb.toString();
633         }
634     }
635 }
636