1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.io.hfile;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.DataInputStream;
22 import java.io.DataOutput;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.ByteBuffer;
27 import java.util.concurrent.locks.Lock;
28 import java.util.concurrent.locks.ReentrantLock;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.hadoop.fs.FSDataInputStream;
33 import org.apache.hadoop.fs.FSDataOutputStream;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.Cell;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.classification.InterfaceAudience;
38 import org.apache.hadoop.hbase.fs.HFileSystem;
39 import org.apache.hadoop.hbase.io.ByteBufferInputStream;
40 import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
41 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
42 import org.apache.hadoop.hbase.io.encoding.HFileBlockDecodingContext;
43 import org.apache.hadoop.hbase.io.encoding.HFileBlockDefaultDecodingContext;
44 import org.apache.hadoop.hbase.io.encoding.HFileBlockDefaultEncodingContext;
45 import org.apache.hadoop.hbase.io.encoding.HFileBlockEncodingContext;
46 import org.apache.hadoop.hbase.util.ByteBufferUtils;
47 import org.apache.hadoop.hbase.util.Bytes;
48 import org.apache.hadoop.hbase.util.ChecksumType;
49 import org.apache.hadoop.hbase.util.ClassSize;
50 import org.apache.hadoop.io.IOUtils;
51
52 import com.google.common.annotations.VisibleForTesting;
53 import com.google.common.base.Preconditions;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 @InterfaceAudience.Private
107 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="HE_EQUALS_USE_HASHCODE",
108 justification="Fix!!! Fine for now bug FIXXXXXXX!!!!")
109 public class HFileBlock implements Cacheable {
110 private static final Log LOG = LogFactory.getLog(HFileBlock.class);
111
112
113 private BlockType blockType;
114
115
116
117
118
119 private int onDiskSizeWithoutHeader;
120
121
122
123
124
125 private int uncompressedSizeWithoutHeader;
126
127
128
129
130
131 private long prevBlockOffset;
132
133
134
135
136
137
138 private int onDiskDataSizeWithHeader;
139
140
141
142
143
144
145
146
147
148
149
150 private ByteBuffer buf;
151
152
153
154 private HFileContext fileContext;
155
156
157
158
159
160 private long offset = UNSET;
161
162
163
164
165
166
167
168
169
170
171
172
173 private int nextBlockOnDiskSize = UNSET;
174
175
176
177
178
179 static final int CHECKSUM_VERIFICATION_NUM_IO_THRESHOLD = 3;
180
181 private static int UNSET = -1;
182 public static final boolean FILL_HEADER = true;
183 public static final boolean DONT_FILL_HEADER = false;
184
185 public static final int BYTE_BUFFER_HEAP_SIZE = (int) ClassSize.estimateBase(
186 ByteBuffer.wrap(new byte[0], 0, 0).getClass(), false);
187
188
189
190
191
192
193
194
195
196
197
198
199
200 static final int BLOCK_METADATA_SPACE = Bytes.SIZEOF_BYTE + Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT;
201
202
203
204
205 static final int CHECKSUM_SIZE = Bytes.SIZEOF_INT;
206
207 static final byte[] DUMMY_HEADER_NO_CHECKSUM =
208 new byte[HConstants.HFILEBLOCK_HEADER_SIZE_NO_CHECKSUM];
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 static final CacheableDeserializer<Cacheable> BLOCK_DESERIALIZER =
225 new CacheableDeserializer<Cacheable>() {
226 public HFileBlock deserialize(ByteBuffer buf, boolean reuse) throws IOException{
227
228
229 buf.limit(buf.limit() - BLOCK_METADATA_SPACE).rewind();
230
231 ByteBuffer newByteBuff;
232 if (reuse) {
233 newByteBuff = buf.slice();
234 } else {
235 int len = buf.limit();
236 newByteBuff = ByteBuffer.allocate(len);
237 ByteBufferUtils.copyFromBufferToBuffer(newByteBuff, buf, buf.position(), 0, len);
238 }
239
240 buf.position(buf.limit());
241 buf.limit(buf.limit() + HFileBlock.BLOCK_METADATA_SPACE);
242 boolean usesChecksum = buf.get() == (byte)1;
243 long offset = buf.getLong();
244 int nextBlockOnDiskSize = buf.getInt();
245 HFileBlock hFileBlock =
246 new HFileBlock(newByteBuff, usesChecksum, offset, nextBlockOnDiskSize, null);
247 return hFileBlock;
248 }
249
250 @Override
251 public int getDeserialiserIdentifier() {
252 return DESERIALIZER_IDENTIFIER;
253 }
254
255 @Override
256 public HFileBlock deserialize(ByteBuffer b) throws IOException {
257 return deserialize(b, false);
258 }
259 };
260 private static final int DESERIALIZER_IDENTIFIER;
261 static {
262 DESERIALIZER_IDENTIFIER =
263 CacheableDeserializerIdManager.registerDeserializer(BLOCK_DESERIALIZER);
264 }
265
266
267 static class Header {
268
269
270
271
272
273
274
275
276
277 static int BLOCK_MAGIC_INDEX = 0;
278 static int ON_DISK_SIZE_WITHOUT_HEADER_INDEX = 8;
279 static int UNCOMPRESSED_SIZE_WITHOUT_HEADER_INDEX = 12;
280 static int PREV_BLOCK_OFFSET_INDEX = 16;
281 static int CHECKSUM_TYPE_INDEX = 24;
282 static int BYTES_PER_CHECKSUM_INDEX = 25;
283 static int ON_DISK_DATA_SIZE_WITH_HEADER_INDEX = 29;
284 }
285
286
287
288
289 private HFileBlock(HFileBlock that) {
290 this.blockType = that.blockType;
291 this.onDiskSizeWithoutHeader = that.onDiskSizeWithoutHeader;
292 this.uncompressedSizeWithoutHeader = that.uncompressedSizeWithoutHeader;
293 this.prevBlockOffset = that.prevBlockOffset;
294 this.buf = that.buf.duplicate();
295 this.offset = that.offset;
296 this.onDiskDataSizeWithHeader = that.onDiskDataSizeWithHeader;
297 this.fileContext = that.fileContext;
298 this.nextBlockOnDiskSize = that.nextBlockOnDiskSize;
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322 HFileBlock(BlockType blockType, int onDiskSizeWithoutHeader, int uncompressedSizeWithoutHeader,
323 long prevBlockOffset, ByteBuffer b, boolean fillHeader, long offset,
324 final int nextBlockOnDiskSize, int onDiskDataSizeWithHeader, HFileContext fileContext) {
325 init(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader,
326 prevBlockOffset, offset, onDiskDataSizeWithHeader, nextBlockOnDiskSize, fileContext);
327 this.buf = b;
328 if (fillHeader) {
329 overwriteHeader();
330 }
331 this.buf.rewind();
332 }
333
334
335
336
337
338
339
340
341 HFileBlock(ByteBuffer buf, boolean usesHBaseChecksum, final long offset,
342 final int nextBlockOnDiskSize, HFileContext fileContext) throws IOException {
343 buf.rewind();
344 final BlockType blockType = BlockType.read(buf);
345 final int onDiskSizeWithoutHeader = buf.getInt(Header.ON_DISK_SIZE_WITHOUT_HEADER_INDEX);
346 final int uncompressedSizeWithoutHeader =
347 buf.getInt(Header.UNCOMPRESSED_SIZE_WITHOUT_HEADER_INDEX);
348 final long prevBlockOffset = buf.getLong(Header.PREV_BLOCK_OFFSET_INDEX);
349
350
351 HFileContextBuilder fileContextBuilder = fileContext != null?
352 new HFileContextBuilder(fileContext): new HFileContextBuilder();
353 fileContextBuilder.withHBaseCheckSum(usesHBaseChecksum);
354 int onDiskDataSizeWithHeader;
355 if (usesHBaseChecksum) {
356 byte checksumType = buf.get(Header.CHECKSUM_TYPE_INDEX);
357 int bytesPerChecksum = buf.getInt(Header.BYTES_PER_CHECKSUM_INDEX);
358 onDiskDataSizeWithHeader = buf.getInt(Header.ON_DISK_DATA_SIZE_WITH_HEADER_INDEX);
359
360 fileContextBuilder.withChecksumType(ChecksumType.codeToType(checksumType));
361 fileContextBuilder.withBytesPerCheckSum(bytesPerChecksum);
362 } else {
363 fileContextBuilder.withChecksumType(ChecksumType.NULL);
364 fileContextBuilder.withBytesPerCheckSum(0);
365
366 onDiskDataSizeWithHeader = onDiskSizeWithoutHeader + headerSize(usesHBaseChecksum);
367 }
368 fileContext = fileContextBuilder.build();
369 assert usesHBaseChecksum == fileContext.isUseHBaseChecksum();
370 init(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader,
371 prevBlockOffset, offset, onDiskDataSizeWithHeader, nextBlockOnDiskSize, fileContext);
372 this.offset = offset;
373 this.buf = buf;
374 this.buf.rewind();
375 }
376
377
378
379
380 private void init(BlockType blockType, int onDiskSizeWithoutHeader,
381 int uncompressedSizeWithoutHeader, long prevBlockOffset,
382 long offset, int onDiskDataSizeWithHeader, final int nextBlockOnDiskSize,
383 HFileContext fileContext) {
384 this.blockType = blockType;
385 this.onDiskSizeWithoutHeader = onDiskSizeWithoutHeader;
386 this.uncompressedSizeWithoutHeader = uncompressedSizeWithoutHeader;
387 this.prevBlockOffset = prevBlockOffset;
388 this.offset = offset;
389 this.onDiskDataSizeWithHeader = onDiskDataSizeWithHeader;
390 this.nextBlockOnDiskSize = nextBlockOnDiskSize;
391 this.fileContext = fileContext;
392 }
393
394
395
396
397
398
399
400 private static int getOnDiskSizeWithHeader(final ByteBuffer headerBuf, boolean verifyChecksum) {
401 return headerBuf.getInt(Header.ON_DISK_SIZE_WITHOUT_HEADER_INDEX) +
402 headerSize(verifyChecksum);
403 }
404
405
406
407
408
409
410 public int getNextBlockOnDiskSize() {
411 return nextBlockOnDiskSize;
412 }
413
414 public BlockType getBlockType() {
415 return blockType;
416 }
417
418
419 public short getDataBlockEncodingId() {
420 if (blockType != BlockType.ENCODED_DATA) {
421 throw new IllegalArgumentException("Querying encoder ID of a block " +
422 "of type other than " + BlockType.ENCODED_DATA + ": " + blockType);
423 }
424 return buf.getShort(headerSize());
425 }
426
427
428
429
430 public int getOnDiskSizeWithHeader() {
431 return onDiskSizeWithoutHeader + headerSize();
432 }
433
434
435
436
437 int getOnDiskSizeWithoutHeader() {
438 return onDiskSizeWithoutHeader;
439 }
440
441
442
443
444 public int getUncompressedSizeWithoutHeader() {
445 return uncompressedSizeWithoutHeader;
446 }
447
448
449
450
451
452 long getPrevBlockOffset() {
453 return prevBlockOffset;
454 }
455
456
457
458
459
460 private void overwriteHeader() {
461 buf.rewind();
462 blockType.write(buf);
463 buf.putInt(onDiskSizeWithoutHeader);
464 buf.putInt(uncompressedSizeWithoutHeader);
465 buf.putLong(prevBlockOffset);
466 if (this.fileContext.isUseHBaseChecksum()) {
467 buf.put(fileContext.getChecksumType().getCode());
468 buf.putInt(fileContext.getBytesPerChecksum());
469 buf.putInt(onDiskDataSizeWithHeader);
470 }
471 }
472
473
474
475
476
477
478 public ByteBuffer getBufferWithoutHeader() {
479 ByteBuffer dup = getBufferReadOnly();
480
481 dup.position(headerSize()).limit(buf.limit() - totalChecksumBytes());
482 return dup.slice();
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496 public ByteBuffer getBufferReadOnly() {
497 ByteBuffer dup = this.buf.duplicate();
498 return dup;
499 }
500
501 private void sanityCheckAssertion(long valueFromBuf, long valueFromField,
502 String fieldName) throws IOException {
503 if (valueFromBuf != valueFromField) {
504 throw new AssertionError(fieldName + " in the buffer (" + valueFromBuf
505 + ") is different from that in the field (" + valueFromField + ")");
506 }
507 }
508
509 private void sanityCheckAssertion(BlockType valueFromBuf, BlockType valueFromField)
510 throws IOException {
511 if (valueFromBuf != valueFromField) {
512 throw new IOException("Block type stored in the buffer: " +
513 valueFromBuf + ", block type field: " + valueFromField);
514 }
515 }
516
517
518
519
520
521
522
523
524
525 @VisibleForTesting
526 void sanityCheck() throws IOException {
527
528 ByteBuffer dup = this.buf.duplicate();
529 dup.rewind();
530 sanityCheckAssertion(BlockType.read(dup), blockType);
531 sanityCheckAssertion(dup.getInt(), onDiskSizeWithoutHeader, "onDiskSizeWithoutHeader");
532 sanityCheckAssertion(dup.getInt(), uncompressedSizeWithoutHeader,
533 "uncompressedSizeWithoutHeader");
534 sanityCheckAssertion(dup.getLong(), prevBlockOffset, "prevBlockOffset");
535 if (this.fileContext.isUseHBaseChecksum()) {
536 sanityCheckAssertion(dup.get(), this.fileContext.getChecksumType().getCode(), "checksumType");
537 sanityCheckAssertion(dup.getInt(), this.fileContext.getBytesPerChecksum(),
538 "bytesPerChecksum");
539 sanityCheckAssertion(dup.getInt(), onDiskDataSizeWithHeader, "onDiskDataSizeWithHeader");
540 }
541
542 int cksumBytes = totalChecksumBytes();
543 int expectedBufLimit = onDiskDataSizeWithHeader + cksumBytes;
544 if (dup.limit() != expectedBufLimit) {
545 throw new AssertionError("Expected limit " + expectedBufLimit + ", got " + dup.limit());
546 }
547
548
549
550 int hdrSize = headerSize();
551 if (dup.capacity() != expectedBufLimit && dup.capacity() != expectedBufLimit + hdrSize) {
552 throw new AssertionError("Invalid buffer capacity: " + dup.capacity() +
553 ", expected " + expectedBufLimit + " or " + (expectedBufLimit + hdrSize));
554 }
555 }
556
557 @Override
558 public String toString() {
559 StringBuilder sb = new StringBuilder()
560 .append("[")
561 .append("blockType=").append(blockType)
562 .append(", fileOffset=").append(offset)
563 .append(", headerSize=").append(headerSize())
564 .append(", onDiskSizeWithoutHeader=").append(onDiskSizeWithoutHeader)
565 .append(", uncompressedSizeWithoutHeader=").append(uncompressedSizeWithoutHeader)
566 .append(", prevBlockOffset=").append(prevBlockOffset)
567 .append(", isUseHBaseChecksum=").append(fileContext.isUseHBaseChecksum());
568 if (fileContext.isUseHBaseChecksum()) {
569 sb.append(", checksumType=").append(ChecksumType.codeToType(this.buf.get(24)))
570 .append(", bytesPerChecksum=").append(this.buf.getInt(24 + 1))
571 .append(", onDiskDataSizeWithHeader=").append(onDiskDataSizeWithHeader);
572 } else {
573 sb.append(", onDiskDataSizeWithHeader=").append(onDiskDataSizeWithHeader)
574 .append("(").append(onDiskSizeWithoutHeader)
575 .append("+").append(HConstants.HFILEBLOCK_HEADER_SIZE_NO_CHECKSUM).append(")");
576 }
577 String dataBegin = null;
578 if (buf.hasArray()) {
579 dataBegin = Bytes.toStringBinary(buf.array(), buf.arrayOffset() + headerSize(),
580 Math.min(32, buf.limit() - buf.arrayOffset() - headerSize()));
581 } else {
582 ByteBuffer bufWithoutHeader = getBufferWithoutHeader();
583 byte[] dataBeginBytes = new byte[Math.min(32,
584 bufWithoutHeader.limit() - bufWithoutHeader.position())];
585 bufWithoutHeader.get(dataBeginBytes);
586 dataBegin = Bytes.toStringBinary(dataBeginBytes);
587 }
588 sb.append(", getOnDiskSizeWithHeader=").append(getOnDiskSizeWithHeader())
589 .append(", totalChecksumBytes=").append(totalChecksumBytes())
590 .append(", isUnpacked=").append(isUnpacked())
591 .append(", buf=[").append(buf).append("]")
592 .append(", dataBeginsWith=").append(dataBegin)
593 .append(", fileContext=").append(fileContext)
594 .append("]");
595 return sb.toString();
596 }
597
598
599
600
601
602 HFileBlock unpack(HFileContext fileContext, FSReader reader) throws IOException {
603 if (!fileContext.isCompressedOrEncrypted()) {
604
605
606
607 return this;
608 }
609
610 HFileBlock unpacked = new HFileBlock(this);
611 unpacked.allocateBuffer();
612
613 HFileBlockDecodingContext ctx = blockType == BlockType.ENCODED_DATA ?
614 reader.getBlockDecodingContext() : reader.getDefaultBlockDecodingContext();
615
616 ByteBuffer dup = this.buf.duplicate();
617 dup.position(this.headerSize());
618 dup = dup.slice();
619 ctx.prepareDecoding(unpacked.getOnDiskSizeWithoutHeader(),
620 unpacked.getUncompressedSizeWithoutHeader(), unpacked.getBufferWithoutHeader(),
621 dup);
622 return unpacked;
623 }
624
625
626
627
628
629
630 private void allocateBuffer() {
631 int cksumBytes = totalChecksumBytes();
632 int headerSize = headerSize();
633 int capacityNeeded = headerSize + uncompressedSizeWithoutHeader + cksumBytes;
634
635
636 ByteBuffer newBuf = ByteBuffer.allocate(capacityNeeded);
637
638
639
640 ByteBuffer dup = buf.duplicate();
641 dup.position(0);
642 dup.get(newBuf.array(), newBuf.arrayOffset(), headerSize);
643
644 buf = newBuf;
645
646 buf.limit(headerSize + uncompressedSizeWithoutHeader + cksumBytes);
647 }
648
649
650
651
652
653 public boolean isUnpacked() {
654 final int cksumBytes = totalChecksumBytes();
655 final int headerSize = headerSize();
656 final int expectedCapacity = headerSize + uncompressedSizeWithoutHeader + cksumBytes;
657 final int bufCapacity = buf.capacity();
658 return bufCapacity == expectedCapacity || bufCapacity == expectedCapacity + headerSize;
659 }
660
661
662 public void sanityCheckUncompressedSize() throws IOException {
663 if (onDiskSizeWithoutHeader != uncompressedSizeWithoutHeader + totalChecksumBytes()) {
664 throw new IOException("Using no compression but "
665 + "onDiskSizeWithoutHeader=" + onDiskSizeWithoutHeader + ", "
666 + "uncompressedSizeWithoutHeader=" + uncompressedSizeWithoutHeader
667 + ", numChecksumbytes=" + totalChecksumBytes());
668 }
669 }
670
671
672
673
674
675
676 long getOffset() {
677 if (offset < 0) {
678 throw new IllegalStateException("HFile block offset not initialized properly");
679 }
680 return offset;
681 }
682
683
684
685
686 public DataInputStream getByteStream() {
687 ByteBuffer dup = this.buf.duplicate();
688 dup.position(this.headerSize());
689 return new DataInputStream(new ByteBufferInputStream(dup));
690 }
691
692 @Override
693 public long heapSize() {
694 long size = ClassSize.align(
695 ClassSize.OBJECT +
696
697 3 * ClassSize.REFERENCE +
698
699
700 4 * Bytes.SIZEOF_INT +
701
702 2 * Bytes.SIZEOF_LONG +
703
704 fileContext.heapSize()
705 );
706
707 if (buf != null) {
708
709 size += ClassSize.align(buf.capacity() + BYTE_BUFFER_HEAP_SIZE);
710 }
711
712 return ClassSize.align(size);
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729 static boolean readWithExtra(InputStream in, byte[] buf,
730 int bufOffset, int necessaryLen, int extraLen) throws IOException {
731 int bytesRemaining = necessaryLen + extraLen;
732 while (bytesRemaining > 0) {
733 int ret = in.read(buf, bufOffset, bytesRemaining);
734 if (ret == -1 && bytesRemaining <= extraLen) {
735
736 break;
737 }
738 if (ret < 0) {
739 throw new IOException("Premature EOF from inputStream (read "
740 + "returned " + ret + ", was trying to read " + necessaryLen
741 + " necessary bytes and " + extraLen + " extra bytes, "
742 + "successfully read "
743 + (necessaryLen + extraLen - bytesRemaining));
744 }
745 bufOffset += ret;
746 bytesRemaining -= ret;
747 }
748 return bytesRemaining <= 0;
749 }
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769 @VisibleForTesting
770 static boolean positionalReadWithExtra(FSDataInputStream in,
771 long position, byte[] buf, int bufOffset, int necessaryLen, int extraLen)
772 throws IOException {
773 int bytesRemaining = necessaryLen + extraLen;
774 int bytesRead = 0;
775 while (bytesRead < necessaryLen) {
776 int ret = in.read(position, buf, bufOffset, bytesRemaining);
777 if (ret < 0) {
778 throw new IOException("Premature EOF from inputStream (positional read "
779 + "returned " + ret + ", was trying to read " + necessaryLen
780 + " necessary bytes and " + extraLen + " extra bytes, "
781 + "successfully read " + bytesRead);
782 }
783 position += ret;
784 bufOffset += ret;
785 bytesRemaining -= ret;
786 bytesRead += ret;
787 }
788 return bytesRead != necessaryLen && bytesRemaining <= 0;
789 }
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804 public static class Writer {
805 private enum State {
806 INIT,
807 WRITING,
808 BLOCK_READY
809 };
810
811
812 private State state = State.INIT;
813
814
815 private final HFileDataBlockEncoder dataBlockEncoder;
816
817 private HFileBlockEncodingContext dataBlockEncodingCtx;
818
819
820 private HFileBlockDefaultEncodingContext defaultBlockEncodingCtx;
821
822
823
824
825
826
827
828 private ByteArrayOutputStream baosInMemory;
829
830
831
832
833
834
835 private BlockType blockType;
836
837
838
839
840
841 private DataOutputStream userDataStream;
842
843
844
845 private int unencodedDataSizeWritten;
846
847
848
849
850
851
852 private byte[] onDiskBlockBytesWithHeader;
853
854
855
856
857
858
859
860 private byte[] onDiskChecksum;
861
862
863
864
865
866
867
868
869 private byte[] uncompressedBlockBytesWithHeader;
870
871
872
873
874
875 private long startOffset;
876
877
878
879
880
881 private long[] prevOffsetByType;
882
883
884 private long prevOffset;
885
886 private HFileContext fileContext;
887
888
889
890
891 public Writer(HFileDataBlockEncoder dataBlockEncoder, HFileContext fileContext) {
892 if (fileContext.getBytesPerChecksum() < HConstants.HFILEBLOCK_HEADER_SIZE) {
893 throw new RuntimeException("Unsupported value of bytesPerChecksum. " +
894 " Minimum is " + HConstants.HFILEBLOCK_HEADER_SIZE + " but the configured value is " +
895 fileContext.getBytesPerChecksum());
896 }
897 this.dataBlockEncoder = dataBlockEncoder != null?
898 dataBlockEncoder: NoOpDataBlockEncoder.INSTANCE;
899 this.dataBlockEncodingCtx = this.dataBlockEncoder.
900 newDataBlockEncodingContext(HConstants.HFILEBLOCK_DUMMY_HEADER, fileContext);
901
902 this.defaultBlockEncodingCtx = new HFileBlockDefaultEncodingContext(null,
903 HConstants.HFILEBLOCK_DUMMY_HEADER, fileContext);
904
905 baosInMemory = new ByteArrayOutputStream();
906 prevOffsetByType = new long[BlockType.values().length];
907 for (int i = 0; i < prevOffsetByType.length; ++i) {
908 prevOffsetByType[i] = UNSET;
909 }
910
911
912 this.fileContext = fileContext;
913 }
914
915
916
917
918
919
920
921 DataOutputStream startWriting(BlockType newBlockType)
922 throws IOException {
923 if (state == State.BLOCK_READY && startOffset != -1) {
924
925
926 prevOffsetByType[blockType.getId()] = startOffset;
927 }
928
929 startOffset = -1;
930 blockType = newBlockType;
931
932 baosInMemory.reset();
933 baosInMemory.write(HConstants.HFILEBLOCK_DUMMY_HEADER);
934
935 state = State.WRITING;
936
937
938 userDataStream = new DataOutputStream(baosInMemory);
939 if (newBlockType == BlockType.DATA) {
940 this.dataBlockEncoder.startBlockEncoding(dataBlockEncodingCtx, userDataStream);
941 }
942 this.unencodedDataSizeWritten = 0;
943 return userDataStream;
944 }
945
946
947
948
949
950
951 void write(Cell cell) throws IOException{
952 expectState(State.WRITING);
953 this.unencodedDataSizeWritten +=
954 this.dataBlockEncoder.encode(cell, dataBlockEncodingCtx, this.userDataStream);
955 }
956
957
958
959
960
961
962
963
964 DataOutputStream getUserDataStream() {
965 expectState(State.WRITING);
966 return userDataStream;
967 }
968
969
970
971
972
973 void ensureBlockReady() throws IOException {
974 Preconditions.checkState(state != State.INIT,
975 "Unexpected state: " + state);
976
977 if (state == State.BLOCK_READY) {
978 return;
979 }
980
981
982 finishBlock();
983 }
984
985
986
987
988
989
990
991 private void finishBlock() throws IOException {
992 if (blockType == BlockType.DATA) {
993 BufferGrabbingByteArrayOutputStream baosInMemoryCopy =
994 new BufferGrabbingByteArrayOutputStream();
995 baosInMemory.writeTo(baosInMemoryCopy);
996 this.dataBlockEncoder.endBlockEncoding(dataBlockEncodingCtx, userDataStream,
997 baosInMemoryCopy.buf, blockType);
998 blockType = dataBlockEncodingCtx.getBlockType();
999 }
1000 userDataStream.flush();
1001
1002
1003 uncompressedBlockBytesWithHeader = baosInMemory.toByteArray();
1004 prevOffset = prevOffsetByType[blockType.getId()];
1005
1006
1007
1008 state = State.BLOCK_READY;
1009 if (blockType == BlockType.DATA || blockType == BlockType.ENCODED_DATA) {
1010 onDiskBlockBytesWithHeader = dataBlockEncodingCtx.
1011 compressAndEncrypt(uncompressedBlockBytesWithHeader);
1012 } else {
1013 onDiskBlockBytesWithHeader = defaultBlockEncodingCtx.
1014 compressAndEncrypt(uncompressedBlockBytesWithHeader);
1015 }
1016
1017 int numBytes = (int) ChecksumUtil.numBytes(
1018 onDiskBlockBytesWithHeader.length,
1019 fileContext.getBytesPerChecksum());
1020
1021
1022 putHeader(onDiskBlockBytesWithHeader, 0,
1023 onDiskBlockBytesWithHeader.length + numBytes,
1024 uncompressedBlockBytesWithHeader.length, onDiskBlockBytesWithHeader.length);
1025
1026
1027 if (onDiskBlockBytesWithHeader != uncompressedBlockBytesWithHeader) {
1028 putHeader(uncompressedBlockBytesWithHeader, 0,
1029 onDiskBlockBytesWithHeader.length + numBytes,
1030 uncompressedBlockBytesWithHeader.length, onDiskBlockBytesWithHeader.length);
1031 }
1032 onDiskChecksum = new byte[numBytes];
1033 ChecksumUtil.generateChecksums(
1034 onDiskBlockBytesWithHeader, 0, onDiskBlockBytesWithHeader.length,
1035 onDiskChecksum, 0, fileContext.getChecksumType(), fileContext.getBytesPerChecksum());
1036 }
1037
1038 public static class BufferGrabbingByteArrayOutputStream extends ByteArrayOutputStream {
1039 private byte[] buf;
1040
1041 @Override
1042 public void write(byte[] b, int off, int len) {
1043 this.buf = b;
1044 }
1045
1046 public byte[] getBuffer() {
1047 return this.buf;
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 private void putHeader(byte[] dest, int offset, int onDiskSize,
1060 int uncompressedSize, int onDiskDataSize) {
1061 offset = blockType.put(dest, offset);
1062 offset = Bytes.putInt(dest, offset, onDiskSize - HConstants.HFILEBLOCK_HEADER_SIZE);
1063 offset = Bytes.putInt(dest, offset, uncompressedSize - HConstants.HFILEBLOCK_HEADER_SIZE);
1064 offset = Bytes.putLong(dest, offset, prevOffset);
1065 offset = Bytes.putByte(dest, offset, fileContext.getChecksumType().getCode());
1066 offset = Bytes.putInt(dest, offset, fileContext.getBytesPerChecksum());
1067 Bytes.putInt(dest, offset, onDiskDataSize);
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078 void writeHeaderAndData(FSDataOutputStream out) throws IOException {
1079 long offset = out.getPos();
1080 if (startOffset != UNSET && offset != startOffset) {
1081 throw new IOException("A " + blockType + " block written to a "
1082 + "stream twice, first at offset " + startOffset + ", then at "
1083 + offset);
1084 }
1085 startOffset = offset;
1086
1087 finishBlockAndWriteHeaderAndData((DataOutputStream) out);
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 protected void finishBlockAndWriteHeaderAndData(DataOutputStream out)
1100 throws IOException {
1101 ensureBlockReady();
1102 out.write(onDiskBlockBytesWithHeader);
1103 out.write(onDiskChecksum);
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 byte[] getHeaderAndDataForTest() throws IOException {
1117 ensureBlockReady();
1118
1119
1120 byte[] output =
1121 new byte[onDiskBlockBytesWithHeader.length
1122 + onDiskChecksum.length];
1123 System.arraycopy(onDiskBlockBytesWithHeader, 0, output, 0,
1124 onDiskBlockBytesWithHeader.length);
1125 System.arraycopy(onDiskChecksum, 0, output,
1126 onDiskBlockBytesWithHeader.length, onDiskChecksum.length);
1127 return output;
1128 }
1129
1130
1131
1132
1133 void release() {
1134 if (dataBlockEncodingCtx != null) {
1135 dataBlockEncodingCtx.close();
1136 dataBlockEncodingCtx = null;
1137 }
1138 if (defaultBlockEncodingCtx != null) {
1139 defaultBlockEncodingCtx.close();
1140 defaultBlockEncodingCtx = null;
1141 }
1142 }
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152 int getOnDiskSizeWithoutHeader() {
1153 expectState(State.BLOCK_READY);
1154 return onDiskBlockBytesWithHeader.length +
1155 onDiskChecksum.length - HConstants.HFILEBLOCK_HEADER_SIZE;
1156 }
1157
1158
1159
1160
1161
1162
1163
1164
1165 int getOnDiskSizeWithHeader() {
1166 expectState(State.BLOCK_READY);
1167 return onDiskBlockBytesWithHeader.length + onDiskChecksum.length;
1168 }
1169
1170
1171
1172
1173 int getUncompressedSizeWithoutHeader() {
1174 expectState(State.BLOCK_READY);
1175 return uncompressedBlockBytesWithHeader.length - HConstants.HFILEBLOCK_HEADER_SIZE;
1176 }
1177
1178
1179
1180
1181 int getUncompressedSizeWithHeader() {
1182 expectState(State.BLOCK_READY);
1183 return uncompressedBlockBytesWithHeader.length;
1184 }
1185
1186
1187 boolean isWriting() {
1188 return state == State.WRITING;
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198 int blockSizeWritten() {
1199 if (state != State.WRITING) return 0;
1200 return this.unencodedDataSizeWritten;
1201 }
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211 ByteBuffer getUncompressedBufferWithHeader() {
1212 expectState(State.BLOCK_READY);
1213 return ByteBuffer.wrap(uncompressedBlockBytesWithHeader);
1214 }
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224 ByteBuffer getOnDiskBufferWithHeader() {
1225 expectState(State.BLOCK_READY);
1226 return ByteBuffer.wrap(onDiskBlockBytesWithHeader);
1227 }
1228
1229 private void expectState(State expectedState) {
1230 if (state != expectedState) {
1231 throw new IllegalStateException("Expected state: " + expectedState +
1232 ", actual state: " + state);
1233 }
1234 }
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246 void writeBlock(BlockWritable bw, FSDataOutputStream out)
1247 throws IOException {
1248 bw.writeToBlock(startWriting(bw.getBlockType()));
1249 writeHeaderAndData(out);
1250 }
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263 HFileBlock getBlockForCaching(CacheConfig cacheConf) {
1264 HFileContext newContext = new HFileContextBuilder()
1265 .withBlockSize(fileContext.getBlocksize())
1266 .withBytesPerCheckSum(0)
1267 .withChecksumType(ChecksumType.NULL)
1268 .withCompression(fileContext.getCompression())
1269 .withDataBlockEncoding(fileContext.getDataBlockEncoding())
1270 .withHBaseCheckSum(fileContext.isUseHBaseChecksum())
1271 .withCompressTags(fileContext.isCompressTags())
1272 .withIncludesMvcc(fileContext.isIncludesMvcc())
1273 .withIncludesTags(fileContext.isIncludesTags())
1274 .build();
1275 return new HFileBlock(blockType, getOnDiskSizeWithoutHeader(),
1276 getUncompressedSizeWithoutHeader(), prevOffset,
1277 cacheConf.shouldCacheCompressed(blockType.getCategory())?
1278 getOnDiskBufferWithHeader() :
1279 getUncompressedBufferWithHeader(),
1280 FILL_HEADER, startOffset, UNSET,
1281 onDiskBlockBytesWithHeader.length + onDiskChecksum.length, newContext);
1282 }
1283 }
1284
1285
1286 interface BlockWritable {
1287
1288
1289 BlockType getBlockType();
1290
1291
1292
1293
1294
1295
1296
1297 void writeToBlock(DataOutput out) throws IOException;
1298 }
1299
1300
1301
1302
1303 interface BlockIterator {
1304
1305
1306
1307
1308 HFileBlock nextBlock() throws IOException;
1309
1310
1311
1312
1313
1314 HFileBlock nextBlockWithBlockType(BlockType blockType) throws IOException;
1315 }
1316
1317
1318 interface FSReader {
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329 HFileBlock readBlockData(long offset, long onDiskSize, boolean pread) throws IOException;
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340 BlockIterator blockRange(long startOffset, long endOffset);
1341
1342
1343 void closeStreams() throws IOException;
1344
1345
1346 HFileBlockDecodingContext getBlockDecodingContext();
1347
1348
1349 HFileBlockDecodingContext getDefaultBlockDecodingContext();
1350 }
1351
1352
1353
1354
1355
1356 private abstract static class AbstractFSReader implements FSReader {
1357
1358
1359
1360 protected long fileSize;
1361
1362
1363 protected final int hdrSize;
1364
1365
1366 protected HFileSystem hfs;
1367
1368 private final Lock streamLock = new ReentrantLock();
1369
1370
1371 public static final int DEFAULT_BUFFER_SIZE = 1 << 20;
1372
1373 protected HFileContext fileContext;
1374
1375 protected String pathName;
1376
1377 public AbstractFSReader(long fileSize, HFileSystem hfs, Path path, HFileContext fileContext)
1378 throws IOException {
1379 this.fileSize = fileSize;
1380 this.hfs = hfs;
1381 if (path != null) {
1382 this.pathName = path.toString();
1383 }
1384 this.fileContext = fileContext;
1385 this.hdrSize = headerSize(fileContext.isUseHBaseChecksum());
1386 }
1387
1388 @Override
1389 public BlockIterator blockRange(final long startOffset,
1390 final long endOffset) {
1391 final FSReader owner = this;
1392 return new BlockIterator() {
1393 private long offset = startOffset;
1394
1395 @Override
1396 public HFileBlock nextBlock() throws IOException {
1397 if (offset >= endOffset)
1398 return null;
1399 HFileBlock b = readBlockData(offset, -1, false);
1400 offset += b.getOnDiskSizeWithHeader();
1401 return b.unpack(fileContext, owner);
1402 }
1403
1404 @Override
1405 public HFileBlock nextBlockWithBlockType(BlockType blockType)
1406 throws IOException {
1407 HFileBlock blk = nextBlock();
1408 if (blk.getBlockType() != blockType) {
1409 throw new IOException("Expected block of type " + blockType
1410 + " but found " + blk.getBlockType());
1411 }
1412 return blk;
1413 }
1414 };
1415 }
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433 protected int readAtOffset(FSDataInputStream istream, byte [] dest, int destOffset, int size,
1434 boolean peekIntoNextBlock, long fileOffset, boolean pread) throws IOException {
1435 if (peekIntoNextBlock && destOffset + size + hdrSize > dest.length) {
1436
1437
1438 throw new IOException("Attempted to read " + size + " bytes and " +
1439 hdrSize + " bytes of next header into a " + dest.length +
1440 "-byte array at offset " + destOffset);
1441 }
1442
1443 if (!pread && streamLock.tryLock()) {
1444
1445 try {
1446 HFileUtil.seekOnMultipleSources(istream, fileOffset);
1447
1448 long realOffset = istream.getPos();
1449 if (realOffset != fileOffset) {
1450 throw new IOException("Tried to seek to " + fileOffset + " to "
1451 + "read " + size + " bytes, but pos=" + realOffset
1452 + " after seek");
1453 }
1454
1455 if (!peekIntoNextBlock) {
1456 IOUtils.readFully(istream, dest, destOffset, size);
1457 return -1;
1458 }
1459
1460
1461 if (!readWithExtra(istream, dest, destOffset, size, hdrSize)) {
1462 return -1;
1463 }
1464 } finally {
1465 streamLock.unlock();
1466 }
1467 } else {
1468
1469 int extraSize = peekIntoNextBlock ? hdrSize : 0;
1470 if (!positionalReadWithExtra(istream, fileOffset, dest, destOffset, size, extraSize)) {
1471 return -1;
1472 }
1473 }
1474
1475 assert peekIntoNextBlock;
1476 return Bytes.toInt(dest, destOffset + size + BlockType.MAGIC_LENGTH) + hdrSize;
1477 }
1478
1479 }
1480
1481
1482
1483
1484
1485 private static class PrefetchedHeader {
1486 long offset = -1;
1487 byte[] header = new byte[HConstants.HFILEBLOCK_HEADER_SIZE];
1488 final ByteBuffer buf = ByteBuffer.wrap(header, 0, HConstants.HFILEBLOCK_HEADER_SIZE);
1489 @Override
1490 public String toString() {
1491 return "offset=" + this.offset + ", header=" + Bytes.toStringBinary(header);
1492 }
1493 }
1494
1495
1496
1497
1498 static class FSReaderImpl extends AbstractFSReader {
1499
1500
1501 protected FSDataInputStreamWrapper streamWrapper;
1502
1503 private HFileBlockDecodingContext encodedBlockDecodingCtx;
1504
1505
1506 private final HFileBlockDefaultDecodingContext defaultDecodingCtx;
1507
1508
1509
1510
1511
1512
1513 private ThreadLocal<PrefetchedHeader> prefetchedHeaderForThread =
1514 new ThreadLocal<PrefetchedHeader>() {
1515 @Override
1516 public PrefetchedHeader initialValue() {
1517 return new PrefetchedHeader();
1518 }
1519 };
1520
1521 public FSReaderImpl(FSDataInputStreamWrapper stream, long fileSize, HFileSystem hfs, Path path,
1522 HFileContext fileContext) throws IOException {
1523 super(fileSize, hfs, path, fileContext);
1524 this.streamWrapper = stream;
1525
1526 this.streamWrapper.prepareForBlockReader(!fileContext.isUseHBaseChecksum());
1527 defaultDecodingCtx = new HFileBlockDefaultDecodingContext(fileContext);
1528 encodedBlockDecodingCtx = defaultDecodingCtx;
1529 }
1530
1531
1532
1533
1534
1535 FSReaderImpl(FSDataInputStream istream, long fileSize, HFileContext fileContext)
1536 throws IOException {
1537 this(new FSDataInputStreamWrapper(istream), fileSize, null, null, fileContext);
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549 @Override
1550 public HFileBlock readBlockData(long offset, long onDiskSizeWithHeaderL, boolean pread)
1551 throws IOException {
1552
1553
1554
1555
1556
1557 boolean doVerificationThruHBaseChecksum = streamWrapper.shouldUseHBaseChecksum();
1558 FSDataInputStream is = streamWrapper.getStream(doVerificationThruHBaseChecksum);
1559
1560 HFileBlock blk = readBlockDataInternal(is, offset,
1561 onDiskSizeWithHeaderL, pread,
1562 doVerificationThruHBaseChecksum);
1563 if (blk == null) {
1564 HFile.LOG.warn("HBase checksum verification failed for file " +
1565 pathName + " at offset " +
1566 offset + " filesize " + fileSize +
1567 ". Retrying read with HDFS checksums turned on...");
1568
1569 if (!doVerificationThruHBaseChecksum) {
1570 String msg = "HBase checksum verification failed for file " +
1571 pathName + " at offset " +
1572 offset + " filesize " + fileSize +
1573 " but this cannot happen because doVerify is " +
1574 doVerificationThruHBaseChecksum;
1575 HFile.LOG.warn(msg);
1576 throw new IOException(msg);
1577 }
1578 HFile.CHECKSUM_FAILURES.increment();
1579
1580
1581
1582
1583
1584
1585
1586 is = this.streamWrapper.fallbackToFsChecksum(CHECKSUM_VERIFICATION_NUM_IO_THRESHOLD);
1587 doVerificationThruHBaseChecksum = false;
1588 blk = readBlockDataInternal(is, offset, onDiskSizeWithHeaderL, pread,
1589 doVerificationThruHBaseChecksum);
1590 if (blk != null) {
1591 HFile.LOG.warn("HDFS checksum verification succeeded for file " +
1592 pathName + " at offset " +
1593 offset + " filesize " + fileSize);
1594 }
1595 }
1596 if (blk == null && !doVerificationThruHBaseChecksum) {
1597 String msg = "readBlockData failed, possibly due to " +
1598 "checksum verification failed for file " + pathName +
1599 " at offset " + offset + " filesize " + fileSize;
1600 HFile.LOG.warn(msg);
1601 throw new IOException(msg);
1602 }
1603
1604
1605
1606
1607
1608
1609
1610
1611 streamWrapper.checksumOk();
1612 return blk;
1613 }
1614
1615
1616
1617
1618
1619 private static int checkAndGetSizeAsInt(final long onDiskSizeWithHeaderL, final int hdrSize)
1620 throws IOException {
1621 if ((onDiskSizeWithHeaderL < hdrSize && onDiskSizeWithHeaderL != -1)
1622 || onDiskSizeWithHeaderL >= Integer.MAX_VALUE) {
1623 throw new IOException("Invalid onDisksize=" + onDiskSizeWithHeaderL
1624 + ": expected to be at least " + hdrSize
1625 + " and at most " + Integer.MAX_VALUE + ", or -1");
1626 }
1627 return (int)onDiskSizeWithHeaderL;
1628 }
1629
1630
1631
1632
1633
1634
1635
1636
1637 private ByteBuffer getCachedHeader(final long offset) {
1638 PrefetchedHeader prefetchedHeader = prefetchedHeaderForThread.get();
1639
1640 return prefetchedHeader != null && prefetchedHeader.offset == offset?
1641 prefetchedHeader.buf: null;
1642 }
1643
1644
1645
1646
1647
1648 private void cacheNextBlockHeader(final long nextBlockOffset,
1649 final byte [] header, final int headerOffset, final int headerLength) {
1650 PrefetchedHeader prefetchedHeader = prefetchedHeaderForThread.get();
1651 prefetchedHeader.offset = nextBlockOffset;
1652 System.arraycopy(header, headerOffset, prefetchedHeader.header, 0, headerLength);
1653 }
1654
1655
1656
1657
1658
1659
1660 private void verifyOnDiskSizeMatchesHeader(final int passedIn, final ByteBuffer headerBuf,
1661 final long offset, boolean verifyChecksum)
1662 throws IOException {
1663
1664 int fromHeader = getOnDiskSizeWithHeader(headerBuf, verifyChecksum);
1665 if (passedIn != fromHeader) {
1666 throw new IOException("Passed in onDiskSizeWithHeader=" + passedIn + " != " + fromHeader +
1667 ", offset=" + offset + ", fileContext=" + this.fileContext);
1668 }
1669 }
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682 protected HFileBlock readBlockDataInternal(FSDataInputStream is, long offset,
1683 long onDiskSizeWithHeaderL, boolean pread, boolean verifyChecksum) throws IOException {
1684 if (offset < 0) {
1685 throw new IOException("Invalid offset=" + offset + " trying to read "
1686 + "block (onDiskSize=" + onDiskSizeWithHeaderL + ")");
1687 }
1688 int onDiskSizeWithHeader = checkAndGetSizeAsInt(onDiskSizeWithHeaderL, hdrSize);
1689 ByteBuffer headerBuf = getCachedHeader(offset);
1690 if (LOG.isTraceEnabled()) {
1691 LOG.trace("Reading " + this.fileContext.getHFileName() + " at offset=" + offset +
1692 ", pread=" + pread + ", verifyChecksum=" + verifyChecksum + ", cachedHeader=" +
1693 headerBuf + ", onDiskSizeWithHeader=" + onDiskSizeWithHeader);
1694 }
1695 if (onDiskSizeWithHeader <= 0) {
1696
1697
1698
1699
1700 if (headerBuf == null) {
1701 headerBuf = ByteBuffer.allocate(hdrSize);
1702 readAtOffset(is, headerBuf.array(), headerBuf.arrayOffset(), hdrSize, false,
1703 offset, pread);
1704 }
1705 onDiskSizeWithHeader = getOnDiskSizeWithHeader(headerBuf,
1706 this.fileContext.isUseHBaseChecksum());
1707 }
1708
1709 int preReadHeaderSize = headerBuf == null? 0 : hdrSize;
1710
1711
1712
1713
1714 byte[] onDiskBlock = new byte[onDiskSizeWithHeader + hdrSize];
1715 int nextBlockOnDiskSize = readAtOffset(is, onDiskBlock, preReadHeaderSize,
1716 onDiskSizeWithHeader - preReadHeaderSize, true, offset + preReadHeaderSize, pread);
1717 if (headerBuf != null) {
1718
1719
1720 System.arraycopy(headerBuf.array(), headerBuf.arrayOffset(), onDiskBlock, 0, hdrSize);
1721 } else {
1722 headerBuf = ByteBuffer.wrap(onDiskBlock, 0, hdrSize);
1723 }
1724
1725
1726 assert onDiskSizeWithHeader > this.hdrSize;
1727 verifyOnDiskSizeMatchesHeader(onDiskSizeWithHeader, headerBuf, offset,
1728 this.fileContext.isUseHBaseChecksum());
1729 ByteBuffer onDiskBlockByteBuffer = ByteBuffer.wrap(onDiskBlock, 0, onDiskSizeWithHeader);
1730
1731 if (verifyChecksum &&
1732 !validateChecksum(offset, onDiskBlockByteBuffer, hdrSize)) {
1733 return null;
1734 }
1735
1736
1737
1738 HFileBlock hFileBlock =
1739 new HFileBlock(onDiskBlockByteBuffer, this.fileContext.isUseHBaseChecksum(), offset,
1740 nextBlockOnDiskSize, fileContext);
1741
1742 if (!fileContext.isCompressedOrEncrypted()) {
1743 hFileBlock.sanityCheckUncompressed();
1744 }
1745 if (LOG.isTraceEnabled()) {
1746 LOG.trace("Read " + hFileBlock);
1747 }
1748
1749 if (nextBlockOnDiskSize != -1) {
1750 cacheNextBlockHeader(offset + hFileBlock.getOnDiskSizeWithHeader(),
1751 onDiskBlock, onDiskSizeWithHeader, hdrSize);
1752 }
1753 return hFileBlock;
1754 }
1755
1756 void setIncludesMemstoreTS(boolean includesMemstoreTS) {
1757 this.fileContext.setIncludesMvcc(includesMemstoreTS);
1758 }
1759
1760 void setDataBlockEncoder(HFileDataBlockEncoder encoder) {
1761 encodedBlockDecodingCtx = encoder.newDataBlockDecodingContext(this.fileContext);
1762 }
1763
1764 @Override
1765 public HFileBlockDecodingContext getBlockDecodingContext() {
1766 return this.encodedBlockDecodingCtx;
1767 }
1768
1769 @Override
1770 public HFileBlockDecodingContext getDefaultBlockDecodingContext() {
1771 return this.defaultDecodingCtx;
1772 }
1773
1774
1775
1776
1777
1778
1779 protected boolean validateChecksum(long offset, ByteBuffer data, int hdrSize)
1780 throws IOException {
1781
1782
1783
1784
1785
1786 if (!fileContext.isUseHBaseChecksum()) {
1787 return false;
1788 }
1789 return ChecksumUtil.validateChecksum(data, pathName, offset, hdrSize);
1790 }
1791
1792 @Override
1793 public void closeStreams() throws IOException {
1794 streamWrapper.close();
1795 }
1796
1797 @Override
1798 public String toString() {
1799 return "hfs=" + hfs + ", path=" + pathName + ", fileContext=" + fileContext;
1800 }
1801 }
1802
1803
1804 void sanityCheckUncompressed() throws IOException {
1805 if (onDiskSizeWithoutHeader != uncompressedSizeWithoutHeader +
1806 totalChecksumBytes()) {
1807 throw new IOException("Using no compression but "
1808 + "onDiskSizeWithoutHeader=" + onDiskSizeWithoutHeader + ", "
1809 + "uncompressedSizeWithoutHeader=" + uncompressedSizeWithoutHeader
1810 + ", numChecksumbytes=" + totalChecksumBytes());
1811 }
1812 }
1813
1814
1815 @Override
1816 public int getSerializedLength() {
1817 if (buf != null) {
1818
1819 return this.buf.limit() + BLOCK_METADATA_SPACE;
1820 }
1821 return 0;
1822 }
1823
1824
1825 @Override
1826 public void serialize(ByteBuffer destination) {
1827
1828
1829 ByteBufferUtils.copyFromBufferToBuffer(destination, this.buf, 0,
1830 getSerializedLength() - BLOCK_METADATA_SPACE);
1831 destination = addMetaData(destination);
1832
1833
1834
1835
1836 destination.flip();
1837 }
1838
1839
1840
1841
1842 public ByteBuffer getMetaData() {
1843 ByteBuffer bb = ByteBuffer.allocate(BLOCK_METADATA_SPACE);
1844 bb = addMetaData(bb);
1845 bb.flip();
1846 return bb;
1847 }
1848
1849
1850
1851
1852
1853 private ByteBuffer addMetaData(final ByteBuffer destination) {
1854 destination.put(this.fileContext.isUseHBaseChecksum() ? (byte) 1 : (byte) 0);
1855 destination.putLong(this.offset);
1856 destination.putInt(this.nextBlockOnDiskSize);
1857 return destination;
1858 }
1859
1860
1861 @Override
1862 public CacheableDeserializer<Cacheable> getDeserializer() {
1863 return HFileBlock.BLOCK_DESERIALIZER;
1864 }
1865
1866 @Override
1867 public boolean equals(Object comparison) {
1868 if (this == comparison) {
1869 return true;
1870 }
1871 if (comparison == null) {
1872 return false;
1873 }
1874 if (comparison.getClass() != this.getClass()) {
1875 return false;
1876 }
1877
1878 HFileBlock castedComparison = (HFileBlock) comparison;
1879
1880 if (castedComparison.blockType != this.blockType) {
1881 return false;
1882 }
1883 if (castedComparison.nextBlockOnDiskSize != this.nextBlockOnDiskSize) {
1884 return false;
1885 }
1886
1887 if (castedComparison.offset != this.offset) {
1888 return false;
1889 }
1890 if (castedComparison.onDiskSizeWithoutHeader != this.onDiskSizeWithoutHeader) {
1891 return false;
1892 }
1893 if (castedComparison.prevBlockOffset != this.prevBlockOffset) {
1894 return false;
1895 }
1896 if (castedComparison.uncompressedSizeWithoutHeader != this.uncompressedSizeWithoutHeader) {
1897 return false;
1898 }
1899 if (ByteBufferUtils.compareTo(this.buf, 0, this.buf.limit(), castedComparison.buf, 0,
1900 castedComparison.buf.limit()) != 0) {
1901 return false;
1902 }
1903 return true;
1904 }
1905
1906 public DataBlockEncoding getDataBlockEncoding() {
1907 if (blockType == BlockType.ENCODED_DATA) {
1908 return DataBlockEncoding.getEncodingById(getDataBlockEncodingId());
1909 }
1910 return DataBlockEncoding.NONE;
1911 }
1912
1913 byte getChecksumType() {
1914 return this.fileContext.getChecksumType().getCode();
1915 }
1916
1917 int getBytesPerChecksum() {
1918 return this.fileContext.getBytesPerChecksum();
1919 }
1920
1921
1922 int getOnDiskDataSizeWithHeader() {
1923 return this.onDiskDataSizeWithHeader;
1924 }
1925
1926
1927
1928
1929
1930 int totalChecksumBytes() {
1931
1932
1933
1934
1935 if (!fileContext.isUseHBaseChecksum() || this.fileContext.getBytesPerChecksum() == 0) {
1936 return 0;
1937 }
1938 return (int) ChecksumUtil.numBytes(onDiskDataSizeWithHeader,
1939 this.fileContext.getBytesPerChecksum());
1940 }
1941
1942
1943
1944
1945 public int headerSize() {
1946 return headerSize(this.fileContext.isUseHBaseChecksum());
1947 }
1948
1949
1950
1951
1952 public static int headerSize(boolean usesHBaseChecksum) {
1953 return usesHBaseChecksum?
1954 HConstants.HFILEBLOCK_HEADER_SIZE: HConstants.HFILEBLOCK_HEADER_SIZE_NO_CHECKSUM;
1955 }
1956
1957
1958
1959
1960 byte[] getDummyHeaderForVersion() {
1961 return getDummyHeaderForVersion(this.fileContext.isUseHBaseChecksum());
1962 }
1963
1964
1965
1966
1967 static private byte[] getDummyHeaderForVersion(boolean usesHBaseChecksum) {
1968 return usesHBaseChecksum? HConstants.HFILEBLOCK_DUMMY_HEADER: DUMMY_HEADER_NO_CHECKSUM;
1969 }
1970
1971
1972
1973
1974
1975 HFileContext getHFileContext() {
1976 return this.fileContext;
1977 }
1978
1979
1980
1981
1982
1983
1984 @VisibleForTesting
1985 static String toStringHeader(ByteBuffer buf) throws IOException {
1986 byte[] magicBuf = new byte[Math.min(buf.limit() - buf.position(), BlockType.MAGIC_LENGTH)];
1987 buf.get(magicBuf);
1988 BlockType bt = BlockType.parse(magicBuf, 0, BlockType.MAGIC_LENGTH);
1989 int compressedBlockSizeNoHeader = buf.getInt();
1990 int uncompressedBlockSizeNoHeader = buf.getInt();
1991 long prevBlockOffset = buf.getLong();
1992 byte cksumtype = buf.get();
1993 long bytesPerChecksum = buf.getInt();
1994 long onDiskDataSizeWithHeader = buf.getInt();
1995 return " Header dump: magic: " + Bytes.toString(magicBuf) +
1996 " blockType " + bt +
1997 " compressedBlockSizeNoHeader " +
1998 compressedBlockSizeNoHeader +
1999 " uncompressedBlockSizeNoHeader " +
2000 uncompressedBlockSizeNoHeader +
2001 " prevBlockOffset " + prevBlockOffset +
2002 " checksumType " + ChecksumType.codeToType(cksumtype) +
2003 " bytesPerChecksum " + bytesPerChecksum +
2004 " onDiskDataSizeWithHeader " + onDiskDataSizeWithHeader;
2005 }
2006 }