1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.hadoop.hbase.io.encoding;
18
19 import java.io.DataInputStream;
20 import java.io.DataOutputStream;
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23
24 import org.apache.hadoop.hbase.Cell;
25 import org.apache.hadoop.hbase.CellComparator;
26 import org.apache.hadoop.hbase.CellUtil;
27 import org.apache.hadoop.hbase.HConstants;
28 import org.apache.hadoop.hbase.KeyValue;
29 import org.apache.hadoop.hbase.KeyValue.KVComparator;
30 import org.apache.hadoop.hbase.KeyValue.SamePrefixComparator;
31 import org.apache.hadoop.hbase.KeyValue.Type;
32 import org.apache.hadoop.hbase.KeyValueUtil;
33 import org.apache.hadoop.hbase.SettableSequenceId;
34 import org.apache.hadoop.hbase.classification.InterfaceAudience;
35 import org.apache.hadoop.hbase.io.HeapSize;
36 import org.apache.hadoop.hbase.io.TagCompressionContext;
37 import org.apache.hadoop.hbase.io.util.LRUDictionary;
38 import org.apache.hadoop.hbase.util.ByteBufferUtils;
39 import org.apache.hadoop.hbase.util.Bytes;
40 import org.apache.hadoop.hbase.util.ClassSize;
41 import org.apache.hadoop.io.WritableUtils;
42
43
44
45
46 @InterfaceAudience.Private
47 abstract class BufferedDataBlockEncoder extends AbstractDataBlockEncoder {
48
49
50
51 private static int INITIAL_KEY_BUFFER_SIZE = 512;
52
53 @Override
54 public ByteBuffer decodeKeyValues(DataInputStream source,
55 HFileBlockDecodingContext blkDecodingCtx) throws IOException {
56 if (blkDecodingCtx.getClass() != HFileBlockDefaultDecodingContext.class) {
57 throw new IOException(this.getClass().getName() + " only accepts "
58 + HFileBlockDefaultDecodingContext.class.getName() + " as the decoding context.");
59 }
60
61 HFileBlockDefaultDecodingContext decodingCtx =
62 (HFileBlockDefaultDecodingContext) blkDecodingCtx;
63 if (decodingCtx.getHFileContext().isIncludesTags()
64 && decodingCtx.getHFileContext().isCompressTags()) {
65 if (decodingCtx.getTagCompressionContext() != null) {
66
67
68 decodingCtx.getTagCompressionContext().clear();
69 } else {
70 try {
71 TagCompressionContext tagCompressionContext = new TagCompressionContext(
72 LRUDictionary.class, Byte.MAX_VALUE);
73 decodingCtx.setTagCompressionContext(tagCompressionContext);
74 } catch (Exception e) {
75 throw new IOException("Failed to initialize TagCompressionContext", e);
76 }
77 }
78 }
79 return internalDecodeKeyValues(source, 0, 0, decodingCtx);
80 }
81
82 protected static class SeekerState implements Cell {
83 protected ByteBuffer currentBuffer;
84 protected TagCompressionContext tagCompressionContext;
85 protected int valueOffset = -1;
86 protected int keyLength;
87 protected int valueLength;
88 protected int lastCommonPrefix;
89 protected int tagsLength = 0;
90 protected int tagsOffset = -1;
91 protected int tagsCompressedLength = 0;
92 protected boolean uncompressTags = true;
93
94
95 protected byte[] keyBuffer = HConstants.EMPTY_BYTE_ARRAY;
96 protected byte[] tagsBuffer = HConstants.EMPTY_BYTE_ARRAY;
97
98 protected long memstoreTS;
99 protected int nextKvOffset;
100 protected KeyValue.KeyOnlyKeyValue currentKey = new KeyValue.KeyOnlyKeyValue();
101
102 public SeekerState() {
103 }
104
105 protected boolean isValid() {
106 return valueOffset != -1;
107 }
108
109 protected void invalidate() {
110 valueOffset = -1;
111 tagsCompressedLength = 0;
112 currentKey = new KeyValue.KeyOnlyKeyValue();
113 uncompressTags = true;
114 currentBuffer = null;
115 }
116
117 protected void ensureSpaceForKey() {
118 if (keyLength > keyBuffer.length) {
119 int newKeyBufferLength = Integer.highestOneBit(Math.max(
120 INITIAL_KEY_BUFFER_SIZE, keyLength) - 1) << 1;
121 byte[] newKeyBuffer = new byte[newKeyBufferLength];
122 System.arraycopy(keyBuffer, 0, newKeyBuffer, 0, keyBuffer.length);
123 keyBuffer = newKeyBuffer;
124 }
125 }
126
127 protected void ensureSpaceForTags() {
128 if (tagsLength > tagsBuffer.length) {
129 int newTagsBufferLength = Integer.highestOneBit(Math.max(
130 INITIAL_KEY_BUFFER_SIZE, tagsLength) - 1) << 1;
131 byte[] newTagsBuffer = new byte[newTagsBufferLength];
132 System.arraycopy(tagsBuffer, 0, newTagsBuffer, 0, tagsBuffer.length);
133 tagsBuffer = newTagsBuffer;
134 }
135 }
136
137 protected void setKey(byte[] keyBuffer, long memTS) {
138 currentKey.setKey(keyBuffer, 0, keyLength);
139 memstoreTS = memTS;
140 }
141
142
143
144
145
146
147 protected void copyFromNext(SeekerState nextState) {
148 if (keyBuffer.length != nextState.keyBuffer.length) {
149 keyBuffer = nextState.keyBuffer.clone();
150 } else if (!isValid()) {
151
152
153 System.arraycopy(nextState.keyBuffer, 0, keyBuffer, 0,
154 nextState.keyLength);
155 } else {
156
157 System.arraycopy(nextState.keyBuffer, nextState.lastCommonPrefix,
158 keyBuffer, nextState.lastCommonPrefix, nextState.keyLength
159 - nextState.lastCommonPrefix);
160 }
161 currentKey = nextState.currentKey;
162
163 valueOffset = nextState.valueOffset;
164 keyLength = nextState.keyLength;
165 valueLength = nextState.valueLength;
166 lastCommonPrefix = nextState.lastCommonPrefix;
167 nextKvOffset = nextState.nextKvOffset;
168 memstoreTS = nextState.memstoreTS;
169 currentBuffer = nextState.currentBuffer;
170 tagsOffset = nextState.tagsOffset;
171 tagsLength = nextState.tagsLength;
172 if (nextState.tagCompressionContext != null) {
173 tagCompressionContext = nextState.tagCompressionContext;
174 }
175 }
176
177 @Override
178 public byte[] getRowArray() {
179 return currentKey.getRowArray();
180 }
181
182 @Override
183 public int getRowOffset() {
184 return Bytes.SIZEOF_SHORT;
185 }
186
187 @Override
188 public short getRowLength() {
189 return currentKey.getRowLength();
190 }
191
192 @Override
193 public byte[] getFamilyArray() {
194 return currentKey.getFamilyArray();
195 }
196
197 @Override
198 public int getFamilyOffset() {
199 return currentKey.getFamilyOffset();
200 }
201
202 @Override
203 public byte getFamilyLength() {
204 return currentKey.getFamilyLength();
205 }
206
207 @Override
208 public byte[] getQualifierArray() {
209 return currentKey.getQualifierArray();
210 }
211
212 @Override
213 public int getQualifierOffset() {
214 return currentKey.getQualifierOffset();
215 }
216
217 @Override
218 public int getQualifierLength() {
219 return currentKey.getQualifierLength();
220 }
221
222 @Override
223 public long getTimestamp() {
224 return currentKey.getTimestamp();
225 }
226
227 @Override
228 public byte getTypeByte() {
229 return currentKey.getTypeByte();
230 }
231
232 @Override
233 public long getMvccVersion() {
234 return memstoreTS;
235 }
236
237 @Override
238 public long getSequenceId() {
239 return memstoreTS;
240 }
241
242 @Override
243 public byte[] getValueArray() {
244 return currentBuffer.array();
245 }
246
247 @Override
248 public int getValueOffset() {
249 return currentBuffer.arrayOffset() + valueOffset;
250 }
251
252 @Override
253 public int getValueLength() {
254 return valueLength;
255 }
256
257 @Override
258 public byte[] getTagsArray() {
259 if (tagCompressionContext != null) {
260 return tagsBuffer;
261 }
262 return currentBuffer.array();
263 }
264
265 @Override
266 public int getTagsOffset() {
267 if (tagCompressionContext != null) {
268 return 0;
269 }
270 return currentBuffer.arrayOffset() + tagsOffset;
271 }
272
273 @Override
274 public int getTagsLength() {
275 return tagsLength;
276 }
277
278 @Override
279 @Deprecated
280 public byte[] getValue() {
281 throw new UnsupportedOperationException("getValue() not supported");
282 }
283
284 @Override
285 @Deprecated
286 public byte[] getFamily() {
287 throw new UnsupportedOperationException("getFamily() not supported");
288 }
289
290 @Override
291 @Deprecated
292 public byte[] getQualifier() {
293 throw new UnsupportedOperationException("getQualifier() not supported");
294 }
295
296 @Override
297 @Deprecated
298 public byte[] getRow() {
299 throw new UnsupportedOperationException("getRow() not supported");
300 }
301
302 @Override
303 public String toString() {
304 return KeyValue.keyToString(this.keyBuffer, 0, KeyValueUtil.keyLength(this)) + "/vlen="
305 + getValueLength() + "/seqid=" + memstoreTS;
306 }
307
308 public Cell shallowCopy() {
309 return new ClonedSeekerState(currentBuffer, keyBuffer, currentKey.getRowLength(),
310 currentKey.getFamilyOffset(), currentKey.getFamilyLength(), keyLength,
311 currentKey.getQualifierOffset(), currentKey.getQualifierLength(),
312 currentKey.getTimestamp(), currentKey.getTypeByte(), valueLength, valueOffset,
313 memstoreTS, tagsOffset, tagsLength, tagCompressionContext, tagsBuffer);
314 }
315 }
316
317
318
319
320
321
322
323
324
325
326
327 protected static class ClonedSeekerState implements Cell, HeapSize, SettableSequenceId {
328 private static final long FIXED_OVERHEAD = ClassSize.align(ClassSize.OBJECT
329 + (4 * ClassSize.REFERENCE) + (2 * Bytes.SIZEOF_LONG) + (7 * Bytes.SIZEOF_INT)
330 + (Bytes.SIZEOF_SHORT) + (2 * Bytes.SIZEOF_BYTE) + (2 * ClassSize.ARRAY));
331 private byte[] keyOnlyBuffer;
332 private ByteBuffer currentBuffer;
333 private short rowLength;
334 private int familyOffset;
335 private byte familyLength;
336 private int qualifierOffset;
337 private int qualifierLength;
338 private long timestamp;
339 private byte typeByte;
340 private int valueOffset;
341 private int valueLength;
342 private int tagsLength;
343 private int tagsOffset;
344 private byte[] cloneTagsBuffer;
345 private long seqId;
346 private TagCompressionContext tagCompressionContext;
347
348 protected ClonedSeekerState(ByteBuffer currentBuffer, byte[] keyBuffer, short rowLength,
349 int familyOffset, byte familyLength, int keyLength, int qualOffset, int qualLength,
350 long timeStamp, byte typeByte, int valueLen, int valueOffset, long seqId,
351 int tagsOffset, int tagsLength, TagCompressionContext tagCompressionContext,
352 byte[] tagsBuffer) {
353 this.currentBuffer = currentBuffer;
354 keyOnlyBuffer = new byte[keyLength];
355 this.tagCompressionContext = tagCompressionContext;
356 this.rowLength = rowLength;
357 this.familyOffset = familyOffset;
358 this.familyLength = familyLength;
359 this.qualifierOffset = qualOffset;
360 this.qualifierLength = qualLength;
361 this.timestamp = timeStamp;
362 this.typeByte = typeByte;
363 this.valueLength = valueLen;
364 this.valueOffset = valueOffset;
365 this.tagsOffset = tagsOffset;
366 this.tagsLength = tagsLength;
367 System.arraycopy(keyBuffer, 0, keyOnlyBuffer, 0, keyLength);
368 if (tagCompressionContext != null) {
369 this.cloneTagsBuffer = new byte[tagsLength];
370 System.arraycopy(tagsBuffer, 0, this.cloneTagsBuffer, 0, tagsLength);
371 }
372 setSequenceId(seqId);
373 }
374
375 @Override
376 public byte[] getRowArray() {
377 return keyOnlyBuffer;
378 }
379
380 @Override
381 public byte[] getFamilyArray() {
382 return keyOnlyBuffer;
383 }
384
385 @Override
386 public byte[] getQualifierArray() {
387 return keyOnlyBuffer;
388 }
389
390 @Override
391 public int getRowOffset() {
392 return Bytes.SIZEOF_SHORT;
393 }
394
395 @Override
396 public short getRowLength() {
397 return rowLength;
398 }
399
400 @Override
401 public int getFamilyOffset() {
402 return familyOffset;
403 }
404
405 @Override
406 public byte getFamilyLength() {
407 return familyLength;
408 }
409
410 @Override
411 public int getQualifierOffset() {
412 return qualifierOffset;
413 }
414
415 @Override
416 public int getQualifierLength() {
417 return qualifierLength;
418 }
419
420 @Override
421 public long getTimestamp() {
422 return timestamp;
423 }
424
425 @Override
426 public byte getTypeByte() {
427 return typeByte;
428 }
429
430 @Override
431 @Deprecated
432 public long getMvccVersion() {
433 return getSequenceId();
434 }
435
436 @Override
437 public long getSequenceId() {
438 return seqId;
439 }
440
441 @Override
442 public byte[] getValueArray() {
443 return currentBuffer.array();
444 }
445
446 @Override
447 public int getValueOffset() {
448 return currentBuffer.arrayOffset() + valueOffset;
449 }
450
451 @Override
452 public int getValueLength() {
453 return valueLength;
454 }
455
456 @Override
457 public byte[] getTagsArray() {
458 if (tagCompressionContext != null) {
459 return cloneTagsBuffer;
460 }
461 return currentBuffer.array();
462 }
463
464 @Override
465 public int getTagsOffset() {
466 if (tagCompressionContext != null) {
467 return 0;
468 }
469 return currentBuffer.arrayOffset() + tagsOffset;
470 }
471
472 @Override
473 public int getTagsLength() {
474 return tagsLength;
475 }
476
477 @Override
478 @Deprecated
479 public byte[] getValue() {
480 return CellUtil.cloneValue(this);
481 }
482
483 @Override
484 @Deprecated
485 public byte[] getFamily() {
486 return CellUtil.cloneFamily(this);
487 }
488
489 @Override
490 @Deprecated
491 public byte[] getQualifier() {
492 return CellUtil.cloneQualifier(this);
493 }
494
495 @Override
496 @Deprecated
497 public byte[] getRow() {
498 return CellUtil.cloneRow(this);
499 }
500
501 @Override
502 public String toString() {
503 return KeyValue.keyToString(this.keyOnlyBuffer, 0, KeyValueUtil.keyLength(this)) + "/vlen="
504 + getValueLength() + "/seqid=" + seqId;
505 }
506
507 @Override
508 public void setSequenceId(long seqId) {
509 this.seqId = seqId;
510 }
511
512 @Override
513 public long heapSize() {
514 return FIXED_OVERHEAD + rowLength + familyLength + qualifierLength + valueLength + tagsLength;
515 }
516 }
517
518 protected abstract static class BufferedEncodedSeeker<STATE extends SeekerState>
519 extends AbstractEncodedSeeker {
520 protected final SamePrefixComparator<byte[]> samePrefixComparator;
521 protected ByteBuffer currentBuffer;
522 protected STATE current, previous;
523 protected TagCompressionContext tagCompressionContext = null;
524
525 public BufferedEncodedSeeker(KVComparator comparator,
526 HFileBlockDecodingContext decodingCtx) {
527 super(comparator, decodingCtx);
528 this.samePrefixComparator = comparator;
529 if (decodingCtx.getHFileContext().isCompressTags()) {
530 try {
531 tagCompressionContext = new TagCompressionContext(LRUDictionary.class, Byte.MAX_VALUE);
532 } catch (Exception e) {
533 throw new RuntimeException("Failed to initialize TagCompressionContext", e);
534 }
535 }
536 current = createSeekerState();
537 previous = createSeekerState();
538 }
539
540 @Override
541 public int compareKey(KVComparator comparator, byte[] key, int offset, int length) {
542 return comparator.compareFlatKey(key, offset, length,
543 current.keyBuffer, 0, current.keyLength);
544 }
545
546 @Override
547 public int compareKey(KVComparator comparator, Cell key) {
548 return comparator.compareOnlyKeyPortion(key,
549 new KeyValue.KeyOnlyKeyValue(current.keyBuffer, 0, current.keyLength));
550 }
551
552 @Override
553 public void setCurrentBuffer(ByteBuffer buffer) {
554 if (this.tagCompressionContext != null) {
555 this.tagCompressionContext.clear();
556 }
557 currentBuffer = buffer;
558 current.currentBuffer = currentBuffer;
559 if(tagCompressionContext != null) {
560 current.tagCompressionContext = tagCompressionContext;
561 }
562 decodeFirst();
563 current.setKey(current.keyBuffer, current.memstoreTS);
564 previous.invalidate();
565 }
566
567 @Override
568 public ByteBuffer getKeyDeepCopy() {
569 ByteBuffer keyBuffer = ByteBuffer.allocate(current.keyLength);
570 keyBuffer.put(current.keyBuffer, 0, current.keyLength);
571 keyBuffer.rewind();
572 return keyBuffer;
573 }
574
575 @Override
576 public ByteBuffer getValueShallowCopy() {
577 ByteBuffer dup = currentBuffer.duplicate();
578 dup.position(current.valueOffset);
579 dup.limit(current.valueOffset + current.valueLength);
580 return dup.slice();
581 }
582
583 @Override
584 public Cell getKeyValue() {
585 return current.shallowCopy();
586 }
587
588 @Override
589 public void rewind() {
590 currentBuffer.rewind();
591 if (tagCompressionContext != null) {
592 tagCompressionContext.clear();
593 }
594 decodeFirst();
595 current.setKey(current.keyBuffer, current.memstoreTS);
596 previous.invalidate();
597 }
598
599 @Override
600 public boolean next() {
601 if (!currentBuffer.hasRemaining()) {
602 return false;
603 }
604 decodeNext();
605 current.setKey(current.keyBuffer, current.memstoreTS);
606 previous.invalidate();
607 return true;
608 }
609
610 protected void decodeTags() {
611 current.tagsLength = ByteBufferUtils.readCompressedInt(currentBuffer);
612 if (tagCompressionContext != null) {
613 if (current.uncompressTags) {
614
615 current.ensureSpaceForTags();
616 try {
617 current.tagsCompressedLength = tagCompressionContext.uncompressTags(currentBuffer,
618 current.tagsBuffer, 0, current.tagsLength);
619 } catch (IOException e) {
620 throw new RuntimeException("Exception while uncompressing tags", e);
621 }
622 } else {
623 ByteBufferUtils.skip(currentBuffer, current.tagsCompressedLength);
624 current.uncompressTags = true;
625 }
626 current.tagsOffset = -1;
627 } else {
628
629
630 current.tagsOffset = currentBuffer.position();
631 ByteBufferUtils.skip(currentBuffer, current.tagsLength);
632 }
633 }
634
635 @Override
636 public int seekToKeyInBlock(byte[] key, int offset, int length, boolean seekBefore) {
637 return seekToKeyInBlock(new KeyValue.KeyOnlyKeyValue(key, offset, length), seekBefore);
638 }
639
640 @Override
641 public int seekToKeyInBlock(Cell seekCell, boolean seekBefore) {
642 int rowCommonPrefix = 0;
643 int familyCommonPrefix = 0;
644 int qualCommonPrefix = 0;
645 previous.invalidate();
646 KeyValue.KeyOnlyKeyValue currentCell = new KeyValue.KeyOnlyKeyValue();
647 do {
648 int comp;
649 if (samePrefixComparator != null) {
650 currentCell.setKey(current.keyBuffer, 0, current.keyLength);
651 if (current.lastCommonPrefix != 0) {
652
653
654
655
656
657 rowCommonPrefix = Math.min(rowCommonPrefix, current.lastCommonPrefix - 2);
658 }
659 if (current.lastCommonPrefix <= 2) {
660 rowCommonPrefix = 0;
661 }
662 rowCommonPrefix += CellComparator.findCommonPrefixInRowPart(seekCell, currentCell,
663 rowCommonPrefix);
664 comp = CellComparator.compareCommonRowPrefix(seekCell, currentCell, rowCommonPrefix);
665 if (comp == 0) {
666 comp = compareTypeBytes(seekCell, currentCell);
667 if (comp == 0) {
668
669 familyCommonPrefix = Math.max(
670 0,
671 Math.min(familyCommonPrefix,
672 current.lastCommonPrefix - (3 + currentCell.getRowLength())));
673 familyCommonPrefix += CellComparator.findCommonPrefixInFamilyPart(seekCell,
674 currentCell, familyCommonPrefix);
675 comp = CellComparator.compareCommonFamilyPrefix(seekCell, currentCell,
676 familyCommonPrefix);
677 if (comp == 0) {
678
679
680 qualCommonPrefix = Math.max(
681 0,
682 Math.min(
683 qualCommonPrefix,
684 current.lastCommonPrefix
685 - (3 + currentCell.getRowLength() + currentCell.getFamilyLength())));
686 qualCommonPrefix += CellComparator.findCommonPrefixInQualifierPart(seekCell,
687 currentCell, qualCommonPrefix);
688 comp = CellComparator.compareCommonQualifierPrefix(seekCell, currentCell,
689 qualCommonPrefix);
690 if (comp == 0) {
691 comp = CellComparator.compareTimestamps(seekCell, currentCell);
692 if (comp == 0) {
693
694
695
696
697
698
699
700
701 comp = (0xff & currentCell.getTypeByte()) - (0xff & seekCell.getTypeByte());
702 }
703 }
704 }
705 }
706 }
707 } else {
708 Cell r = new KeyValue.KeyOnlyKeyValue(current.keyBuffer, 0, current.keyLength);
709 comp = comparator.compareOnlyKeyPortion(seekCell, r);
710 }
711 if (comp == 0) {
712 if (seekBefore) {
713 if (!previous.isValid()) {
714
715
716 throw new IllegalStateException("Cannot seekBefore if "
717 + "positioned at the first key in the block: key="
718 + Bytes.toStringBinary(seekCell.getRowArray()));
719 }
720 moveToPrevious();
721 return 1;
722 }
723 return 0;
724 }
725
726 if (comp < 0) {
727 if (previous.isValid()) {
728 moveToPrevious();
729 } else {
730 return HConstants.INDEX_KEY_MAGIC;
731 }
732 return 1;
733 }
734
735
736 if (currentBuffer.hasRemaining()) {
737 previous.copyFromNext(current);
738 decodeNext();
739 current.setKey(current.keyBuffer, current.memstoreTS);
740 } else {
741 break;
742 }
743 } while (true);
744
745
746 return 1;
747 }
748
749 private int compareTypeBytes(Cell key, Cell right) {
750 if (key.getFamilyLength() + key.getQualifierLength() == 0
751 && key.getTypeByte() == Type.Minimum.getCode()) {
752
753 return 1;
754 }
755 if (right.getFamilyLength() + right.getQualifierLength() == 0
756 && right.getTypeByte() == Type.Minimum.getCode()) {
757 return -1;
758 }
759 return 0;
760 }
761
762
763 private void moveToPrevious() {
764 if (!previous.isValid()) {
765 throw new IllegalStateException(
766 "Can move back only once and not in first key in the block.");
767 }
768
769 STATE tmp = previous;
770 previous = current;
771 current = tmp;
772
773
774 currentBuffer.position(current.nextKvOffset);
775
776
777
778
779
780
781 current.tagsBuffer = previous.tagsBuffer;
782 current.tagsCompressedLength = previous.tagsCompressedLength;
783 current.uncompressTags = false;
784 current.setKey(current.keyBuffer, current.memstoreTS);
785 previous.invalidate();
786 }
787
788 @SuppressWarnings("unchecked")
789 protected STATE createSeekerState() {
790
791
792 return (STATE) new SeekerState();
793 }
794
795 abstract protected void decodeFirst();
796 abstract protected void decodeNext();
797 }
798
799
800
801
802
803
804
805
806 protected final int afterEncodingKeyValue(Cell cell, DataOutputStream out,
807 HFileBlockDefaultEncodingContext encodingCtx) throws IOException {
808 int size = 0;
809 if (encodingCtx.getHFileContext().isIncludesTags()) {
810 int tagsLength = cell.getTagsLength();
811 ByteBufferUtils.putCompressedInt(out, tagsLength);
812
813 if (tagsLength > 0) {
814 TagCompressionContext tagCompressionContext = encodingCtx.getTagCompressionContext();
815
816
817 if (tagCompressionContext != null) {
818 tagCompressionContext
819 .compressTags(out, cell.getTagsArray(), cell.getTagsOffset(), tagsLength);
820 } else {
821 out.write(cell.getTagsArray(), cell.getTagsOffset(), tagsLength);
822 }
823 }
824 size += tagsLength + KeyValue.TAGS_LENGTH_SIZE;
825 }
826 if (encodingCtx.getHFileContext().isIncludesMvcc()) {
827
828 long memstoreTS = cell.getSequenceId();
829 WritableUtils.writeVLong(out, memstoreTS);
830
831
832 size += WritableUtils.getVIntSize(memstoreTS);
833 }
834 return size;
835 }
836
837 protected final void afterDecodingKeyValue(DataInputStream source,
838 ByteBuffer dest, HFileBlockDefaultDecodingContext decodingCtx) throws IOException {
839 if (decodingCtx.getHFileContext().isIncludesTags()) {
840 int tagsLength = ByteBufferUtils.readCompressedInt(source);
841
842 dest.put((byte) ((tagsLength >> 8) & 0xff));
843 dest.put((byte) (tagsLength & 0xff));
844 if (tagsLength > 0) {
845 TagCompressionContext tagCompressionContext = decodingCtx.getTagCompressionContext();
846
847
848 if (tagCompressionContext != null) {
849 tagCompressionContext.uncompressTags(source, dest, tagsLength);
850 } else {
851 ByteBufferUtils.copyFromStreamToBuffer(dest, source, tagsLength);
852 }
853 }
854 }
855 if (decodingCtx.getHFileContext().isIncludesMvcc()) {
856 long memstoreTS = -1;
857 try {
858
859
860 memstoreTS = WritableUtils.readVLong(source);
861 ByteBufferUtils.writeVLong(dest, memstoreTS);
862 } catch (IOException ex) {
863 throw new RuntimeException("Unable to copy memstore timestamp " +
864 memstoreTS + " after decoding a key/value");
865 }
866 }
867 }
868
869 protected abstract ByteBuffer internalDecodeKeyValues(DataInputStream source,
870 int allocateHeaderLength, int skipLastBytes, HFileBlockDefaultDecodingContext decodingCtx)
871 throws IOException;
872
873
874
875
876
877
878
879
880 protected static void ensureSpace(ByteBuffer out, int length)
881 throws EncoderBufferTooSmallException {
882 if (out.position() + length > out.limit()) {
883 throw new EncoderBufferTooSmallException(
884 "Buffer position=" + out.position() +
885 ", buffer limit=" + out.limit() +
886 ", length to be written=" + length);
887 }
888 }
889
890 @Override
891 public void startBlockEncoding(HFileBlockEncodingContext blkEncodingCtx, DataOutputStream out)
892 throws IOException {
893 if (blkEncodingCtx.getClass() != HFileBlockDefaultEncodingContext.class) {
894 throw new IOException (this.getClass().getName() + " only accepts "
895 + HFileBlockDefaultEncodingContext.class.getName() + " as the " +
896 "encoding context.");
897 }
898
899 HFileBlockDefaultEncodingContext encodingCtx =
900 (HFileBlockDefaultEncodingContext) blkEncodingCtx;
901 encodingCtx.prepareEncoding(out);
902 if (encodingCtx.getHFileContext().isIncludesTags()
903 && encodingCtx.getHFileContext().isCompressTags()) {
904 if (encodingCtx.getTagCompressionContext() != null) {
905
906
907 encodingCtx.getTagCompressionContext().clear();
908 } else {
909 try {
910 TagCompressionContext tagCompressionContext = new TagCompressionContext(
911 LRUDictionary.class, Byte.MAX_VALUE);
912 encodingCtx.setTagCompressionContext(tagCompressionContext);
913 } catch (Exception e) {
914 throw new IOException("Failed to initialize TagCompressionContext", e);
915 }
916 }
917 }
918 ByteBufferUtils.putInt(out, 0);
919 blkEncodingCtx.setEncodingState(new BufferedDataBlockEncodingState());
920 }
921
922 private static class BufferedDataBlockEncodingState extends EncodingState {
923 int unencodedDataSizeWritten = 0;
924 }
925
926 @Override
927 public int encode(Cell cell, HFileBlockEncodingContext encodingCtx, DataOutputStream out)
928 throws IOException {
929 BufferedDataBlockEncodingState state = (BufferedDataBlockEncodingState) encodingCtx
930 .getEncodingState();
931 int encodedKvSize = internalEncode(cell, (HFileBlockDefaultEncodingContext) encodingCtx, out);
932 state.unencodedDataSizeWritten += encodedKvSize;
933 return encodedKvSize;
934 }
935
936 public abstract int internalEncode(Cell cell, HFileBlockDefaultEncodingContext encodingCtx,
937 DataOutputStream out) throws IOException;
938
939 @Override
940 public void endBlockEncoding(HFileBlockEncodingContext encodingCtx, DataOutputStream out,
941 byte[] uncompressedBytesWithHeader) throws IOException {
942 BufferedDataBlockEncodingState state = (BufferedDataBlockEncodingState) encodingCtx
943 .getEncodingState();
944
945 Bytes.putInt(uncompressedBytesWithHeader,
946 HConstants.HFILEBLOCK_HEADER_SIZE + DataBlockEncoding.ID_SIZE, state.unencodedDataSizeWritten
947 );
948 postEncoding(encodingCtx);
949 }
950 }