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