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