2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.nio
;
20 import static org
.apache
.hadoop
.hbase
.io
.ByteBuffAllocator
.NONE
;
22 import java
.io
.IOException
;
23 import java
.nio
.BufferOverflowException
;
24 import java
.nio
.BufferUnderflowException
;
25 import java
.nio
.ByteBuffer
;
26 import java
.nio
.InvalidMarkException
;
27 import java
.nio
.channels
.FileChannel
;
28 import java
.nio
.channels
.ReadableByteChannel
;
29 import java
.util
.Iterator
;
30 import java
.util
.NoSuchElementException
;
32 import org
.apache
.hadoop
.hbase
.io
.ByteBuffAllocator
.Recycler
;
33 import org
.apache
.hadoop
.hbase
.util
.ByteBufferUtils
;
34 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
35 import org
.apache
.hadoop
.hbase
.util
.ObjectIntPair
;
36 import org
.apache
.yetus
.audience
.InterfaceAudience
;
39 * Provides a unified view of all the underlying ByteBuffers and will look as if a bigger
40 * sequential buffer. This class provides similar APIs as in {@link ByteBuffer} to put/get int,
41 * short, long etc and doing operations like mark, reset, slice etc. This has to be used when
42 * data is split across multiple byte buffers and we don't want copy them to single buffer
43 * for reading from it.
45 @InterfaceAudience.Private
46 public class MultiByteBuff
extends ByteBuff
{
48 private final ByteBuffer
[] items
;
49 // Pointer to the current item in the MBB
50 private ByteBuffer curItem
= null;
51 // Index of the current item in the MBB
52 private int curItemIndex
= 0;
54 private int limit
= 0;
55 private int limitedItemIndex
;
56 private int markedItemIndex
= -1;
57 private final int[] itemBeginPos
;
59 private Iterator
<ByteBuffer
> buffsIterator
= new Iterator
<ByteBuffer
>() {
61 public boolean hasNext() {
62 return curItemIndex
< limitedItemIndex
||
63 (curItemIndex
== limitedItemIndex
&& items
[curItemIndex
].hasRemaining());
67 public ByteBuffer
next() {
68 if (curItemIndex
>= items
.length
) {
69 throw new NoSuchElementException("items overflow");
71 curItem
= items
[curItemIndex
++];
76 public MultiByteBuff(ByteBuffer
... items
) {
80 public MultiByteBuff(Recycler recycler
, ByteBuffer
... items
) {
81 this(new RefCnt(recycler
), items
);
84 MultiByteBuff(RefCnt refCnt
, ByteBuffer
... items
) {
87 assert items
.length
> 0;
89 this.curItem
= this.items
[this.curItemIndex
];
90 // See below optimization in getInt(int) where we check whether the given index land in current
91 // item. For this we need to check whether the passed index is less than the next item begin
92 // offset. To handle this effectively for the last item buffer, we add an extra item into this
94 itemBeginPos
= new int[items
.length
+ 1];
96 for (int i
= 0; i
< items
.length
; i
++) {
97 ByteBuffer item
= items
[i
];
99 itemBeginPos
[i
] = offset
;
100 int l
= item
.limit() - item
.position();
104 this.itemBeginPos
[items
.length
] = offset
+ 1;
105 this.limitedItemIndex
= this.items
.length
- 1;
108 private MultiByteBuff(RefCnt refCnt
, ByteBuffer
[] items
, int[] itemBeginPos
, int limit
,
109 int limitedIndex
, int curItemIndex
, int markedIndex
) {
110 this.refCnt
= refCnt
;
112 this.curItemIndex
= curItemIndex
;
113 this.curItem
= this.items
[this.curItemIndex
];
114 this.itemBeginPos
= itemBeginPos
;
116 this.limitedItemIndex
= limitedIndex
;
117 this.markedItemIndex
= markedIndex
;
121 * @throws UnsupportedOperationException MBB does not support
122 * array based operations
125 public byte[] array() {
126 throw new UnsupportedOperationException();
130 * @throws UnsupportedOperationException MBB does not
131 * support array based operations
134 public int arrayOffset() {
135 throw new UnsupportedOperationException();
139 * @return false. MBB does not support array based operations
142 public boolean hasArray() {
147 * @return the total capacity of this MultiByteBuffer.
150 public int capacity() {
153 for (ByteBuffer item
: this.items
) {
154 c
+= item
.capacity();
160 * Fetches the byte at the given index. Does not change position of the underlying ByteBuffers
162 * @return the byte at the given index
165 public byte get(int index
) {
167 int itemIndex
= getItemIndex(index
);
168 return ByteBufferUtils
.toByte(this.items
[itemIndex
], index
- this.itemBeginPos
[itemIndex
]);
172 public byte getByteAfterPosition(int offset
) {
174 // Mostly the index specified will land within this current item. Short circuit for that
175 int index
= offset
+ this.position();
176 int itemIndex
= getItemIndexFromCurItemIndex(index
);
177 return ByteBufferUtils
.toByte(this.items
[itemIndex
], index
- this.itemBeginPos
[itemIndex
]);
181 * Returns in which sub ByteBuffer, the given element index will be available.
183 private int getItemIndex(int elemIndex
) {
185 while (elemIndex
>= this.itemBeginPos
[index
]) {
187 if (index
== this.itemBeginPos
.length
) {
188 throw new IndexOutOfBoundsException();
195 * Returns in which sub ByteBuffer, the given element index will be available. In this case we are
196 * sure that the item will be after MBB's current position
198 private int getItemIndexFromCurItemIndex(int elemIndex
) {
199 int index
= this.curItemIndex
;
200 while (elemIndex
>= this.itemBeginPos
[index
]) {
202 if (index
== this.itemBeginPos
.length
) {
203 throw new IndexOutOfBoundsException();
210 * Fetches the int at the given index. Does not change position of the underlying ByteBuffers
212 * @return the int value at the given index
215 public int getInt(int index
) {
217 // Mostly the index specified will land within this current item. Short circuit for that
219 if (this.itemBeginPos
[this.curItemIndex
] <= index
220 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
221 itemIndex
= this.curItemIndex
;
223 itemIndex
= getItemIndex(index
);
225 return getInt(index
, itemIndex
);
229 public int getIntAfterPosition(int offset
) {
231 // Mostly the index specified will land within this current item. Short circuit for that
232 int index
= offset
+ this.position();
234 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
235 itemIndex
= this.curItemIndex
;
237 itemIndex
= getItemIndexFromCurItemIndex(index
);
239 return getInt(index
, itemIndex
);
243 * Fetches the short at the given index. Does not change position of the underlying ByteBuffers
245 * @return the short value at the given index
248 public short getShort(int index
) {
250 // Mostly the index specified will land within this current item. Short circuit for that
252 if (this.itemBeginPos
[this.curItemIndex
] <= index
253 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
254 itemIndex
= this.curItemIndex
;
256 itemIndex
= getItemIndex(index
);
258 ByteBuffer item
= items
[itemIndex
];
259 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
260 if (item
.limit() - offsetInItem
>= Bytes
.SIZEOF_SHORT
) {
261 return ByteBufferUtils
.toShort(item
, offsetInItem
);
263 if (items
.length
- 1 == itemIndex
) {
264 // means cur item is the last one and we wont be able to read a int. Throw exception
265 throw new BufferUnderflowException();
267 ByteBuffer nextItem
= items
[itemIndex
+ 1];
268 // Get available one byte from this item and remaining one from next
270 n
= (short) (n ^
(ByteBufferUtils
.toByte(item
, offsetInItem
) & 0xFF));
271 n
= (short) (n
<< 8);
272 n
= (short) (n ^
(ByteBufferUtils
.toByte(nextItem
, 0) & 0xFF));
277 public short getShortAfterPosition(int offset
) {
279 // Mostly the index specified will land within this current item. Short circuit for that
280 int index
= offset
+ this.position();
282 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
283 itemIndex
= this.curItemIndex
;
285 itemIndex
= getItemIndexFromCurItemIndex(index
);
287 return getShort(index
, itemIndex
);
290 private int getInt(int index
, int itemIndex
) {
291 ByteBuffer item
= items
[itemIndex
];
292 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
293 int remainingLen
= item
.limit() - offsetInItem
;
294 if (remainingLen
>= Bytes
.SIZEOF_INT
) {
295 return ByteBufferUtils
.toInt(item
, offsetInItem
);
297 if (items
.length
- 1 == itemIndex
) {
298 // means cur item is the last one and we wont be able to read a int. Throw exception
299 throw new BufferUnderflowException();
302 for (int i
= 0; i
< Bytes
.SIZEOF_INT
; i
++) {
304 l ^
= get(index
+ i
) & 0xFF;
309 private short getShort(int index
, int itemIndex
) {
310 ByteBuffer item
= items
[itemIndex
];
311 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
312 int remainingLen
= item
.limit() - offsetInItem
;
313 if (remainingLen
>= Bytes
.SIZEOF_SHORT
) {
314 return ByteBufferUtils
.toShort(item
, offsetInItem
);
316 if (items
.length
- 1 == itemIndex
) {
317 // means cur item is the last one and we wont be able to read a short. Throw exception
318 throw new BufferUnderflowException();
320 ByteBuffer nextItem
= items
[itemIndex
+ 1];
321 // Get available bytes from this item and remaining from next
323 for (int i
= offsetInItem
; i
< item
.capacity(); i
++) {
324 l
= (short) (l
<< 8);
325 l
= (short) (l ^
(ByteBufferUtils
.toByte(item
, i
) & 0xFF));
327 for (int i
= 0; i
< Bytes
.SIZEOF_SHORT
- remainingLen
; i
++) {
328 l
= (short) (l
<< 8);
329 l
= (short) (l ^
(ByteBufferUtils
.toByte(nextItem
, i
) & 0xFF));
334 private long getLong(int index
, int itemIndex
) {
335 ByteBuffer item
= items
[itemIndex
];
336 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
337 int remainingLen
= item
.limit() - offsetInItem
;
338 if (remainingLen
>= Bytes
.SIZEOF_LONG
) {
339 return ByteBufferUtils
.toLong(item
, offsetInItem
);
341 if (items
.length
- 1 == itemIndex
) {
342 // means cur item is the last one and we wont be able to read a long. Throw exception
343 throw new BufferUnderflowException();
346 for (int i
= 0; i
< Bytes
.SIZEOF_LONG
; i
++) {
348 l ^
= get(index
+ i
) & 0xFF;
354 * Fetches the long at the given index. Does not change position of the underlying ByteBuffers
356 * @return the long value at the given index
359 public long getLong(int index
) {
361 // Mostly the index specified will land within this current item. Short circuit for that
363 if (this.itemBeginPos
[this.curItemIndex
] <= index
364 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
365 itemIndex
= this.curItemIndex
;
367 itemIndex
= getItemIndex(index
);
369 return getLong(index
, itemIndex
);
373 public long getLongAfterPosition(int offset
) {
375 // Mostly the index specified will land within this current item. Short circuit for that
376 int index
= offset
+ this.position();
378 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
379 itemIndex
= this.curItemIndex
;
381 itemIndex
= getItemIndexFromCurItemIndex(index
);
383 return getLong(index
, itemIndex
);
387 * @return this MBB's current position
390 public int position() {
392 return itemBeginPos
[this.curItemIndex
] + this.curItem
.position();
396 * Sets this MBB's position to the given value.
398 * @return this object
401 public MultiByteBuff
position(int position
) {
403 // Short circuit for positioning within the cur item. Mostly that is the case.
404 if (this.itemBeginPos
[this.curItemIndex
] <= position
405 && this.itemBeginPos
[this.curItemIndex
+ 1] > position
) {
406 this.curItem
.position(position
- this.itemBeginPos
[this.curItemIndex
]);
409 int itemIndex
= getItemIndex(position
);
410 // All items from 0 - curItem-1 set position at end.
411 for (int i
= 0; i
< itemIndex
; i
++) {
412 this.items
[i
].position(this.items
[i
].limit());
414 // All items after curItem set position at begin
415 for (int i
= itemIndex
+ 1; i
< this.items
.length
; i
++) {
416 this.items
[i
].position(0);
418 this.curItem
= this.items
[itemIndex
];
419 this.curItem
.position(position
- this.itemBeginPos
[itemIndex
]);
420 this.curItemIndex
= itemIndex
;
425 * Rewinds this MBB and the position is set to 0
426 * @return this object
429 public MultiByteBuff
rewind() {
431 for (int i
= 0; i
< this.items
.length
; i
++) {
432 this.items
[i
].rewind();
434 this.curItemIndex
= 0;
435 this.curItem
= this.items
[this.curItemIndex
];
436 this.markedItemIndex
= -1;
441 * Marks the current position of the MBB
442 * @return this object
445 public MultiByteBuff
mark() {
447 this.markedItemIndex
= this.curItemIndex
;
453 * Similar to {@link ByteBuffer}.reset(), ensures that this MBB
454 * is reset back to last marked position.
458 public MultiByteBuff
reset() {
460 // when the buffer is moved to the next one.. the reset should happen on the previous marked
461 // item and the new one should be taken as the base
462 if (this.markedItemIndex
< 0) throw new InvalidMarkException();
463 ByteBuffer markedItem
= this.items
[this.markedItemIndex
];
465 this.curItem
= markedItem
;
466 // All items after the marked position upto the current item should be reset to 0
467 for (int i
= this.curItemIndex
; i
> this.markedItemIndex
; i
--) {
468 this.items
[i
].position(0);
470 this.curItemIndex
= this.markedItemIndex
;
475 * Returns the number of elements between the current position and the
477 * @return the remaining elements in this MBB
480 public int remaining() {
483 for (int i
= curItemIndex
; i
< items
.length
; i
++) {
484 remain
+= items
[i
].remaining();
490 * Returns true if there are elements between the current position and the limt
491 * @return true if there are elements, false otherwise
494 public final boolean hasRemaining() {
496 return this.curItem
.hasRemaining() || (this.curItemIndex
< this.limitedItemIndex
497 && this.items
[this.curItemIndex
+ 1].hasRemaining());
501 * A relative method that returns byte at the current position. Increments the
502 * current position by the size of a byte.
503 * @return the byte at the current position
508 if (this.curItem
.remaining() == 0) {
509 if (items
.length
- 1 == this.curItemIndex
) {
510 // means cur item is the last one and we wont be able to read a long. Throw exception
511 throw new BufferUnderflowException();
514 this.curItem
= this.items
[this.curItemIndex
];
516 return this.curItem
.get();
520 * Returns the short value at the current position. Also advances the position by the size
523 * @return the short value at the current position
526 public short getShort() {
528 int remaining
= this.curItem
.remaining();
529 if (remaining
>= Bytes
.SIZEOF_SHORT
) {
530 return this.curItem
.getShort();
533 n
= (short) (n ^
(get() & 0xFF));
534 n
= (short) (n
<< 8);
535 n
= (short) (n ^
(get() & 0xFF));
540 * Returns the int value at the current position. Also advances the position by the size of int
542 * @return the int value at the current position
545 public int getInt() {
547 int remaining
= this.curItem
.remaining();
548 if (remaining
>= Bytes
.SIZEOF_INT
) {
549 return this.curItem
.getInt();
552 for (int i
= 0; i
< Bytes
.SIZEOF_INT
; i
++) {
561 * Returns the long value at the current position. Also advances the position by the size of long
563 * @return the long value at the current position
566 public long getLong() {
568 int remaining
= this.curItem
.remaining();
569 if (remaining
>= Bytes
.SIZEOF_LONG
) {
570 return this.curItem
.getLong();
573 for (int i
= 0; i
< Bytes
.SIZEOF_LONG
; i
++) {
581 * Copies the content from this MBB's current position to the byte array and fills it. Also
582 * advances the position of the MBB by the length of the byte[].
586 public void get(byte[] dst
) {
587 get(dst
, 0, dst
.length
);
591 * Copies the specified number of bytes from this MBB's current position to the byte[]'s offset.
592 * Also advances the position of the MBB by the given length.
594 * @param offset within the current array
595 * @param length upto which the bytes to be copied
598 public void get(byte[] dst
, int offset
, int length
) {
601 int toRead
= Math
.min(length
, this.curItem
.remaining());
602 ByteBufferUtils
.copyFromBufferToArray(dst
, this.curItem
, this.curItem
.position(), offset
,
604 this.curItem
.position(this.curItem
.position() + toRead
);
606 if (length
== 0) break;
608 this.curItem
= this.items
[this.curItemIndex
];
614 public void get(int sourceOffset
, byte[] dst
, int offset
, int length
) {
616 int itemIndex
= getItemIndex(sourceOffset
);
617 ByteBuffer item
= this.items
[itemIndex
];
618 sourceOffset
= sourceOffset
- this.itemBeginPos
[itemIndex
];
620 int toRead
= Math
.min((item
.limit() - sourceOffset
), length
);
621 ByteBufferUtils
.copyFromBufferToArray(dst
, item
, sourceOffset
, offset
, toRead
);
623 if (length
== 0) break;
625 item
= this.items
[itemIndex
];
632 * Marks the limit of this MBB.
637 public MultiByteBuff
limit(int limit
) {
640 // Normally the limit will try to limit within the last BB item
641 int limitedIndexBegin
= this.itemBeginPos
[this.limitedItemIndex
];
642 if (limit
>= limitedIndexBegin
&& limit
< this.itemBeginPos
[this.limitedItemIndex
+ 1]) {
643 this.items
[this.limitedItemIndex
].limit(limit
- limitedIndexBegin
);
646 int itemIndex
= getItemIndex(limit
);
647 int beginOffset
= this.itemBeginPos
[itemIndex
];
648 int offsetInItem
= limit
- beginOffset
;
649 ByteBuffer item
= items
[itemIndex
];
650 item
.limit(offsetInItem
);
651 for (int i
= this.limitedItemIndex
; i
< itemIndex
; i
++) {
652 this.items
[i
].limit(this.items
[i
].capacity());
654 this.limitedItemIndex
= itemIndex
;
655 for (int i
= itemIndex
+ 1; i
< this.items
.length
; i
++) {
656 this.items
[i
].limit(this.items
[i
].position());
662 * Returns the limit of this MBB
663 * @return limit of the MBB
671 * Returns an MBB which is a sliced version of this MBB. The position, limit and mark
672 * of the new MBB will be independent than that of the original MBB.
673 * The content of the new MBB will start at this MBB's current position
674 * @return a sliced MBB
677 public MultiByteBuff
slice() {
679 ByteBuffer
[] copy
= new ByteBuffer
[this.limitedItemIndex
- this.curItemIndex
+ 1];
680 for (int i
= curItemIndex
, j
= 0; i
<= this.limitedItemIndex
; i
++, j
++) {
681 copy
[j
] = this.items
[i
].slice();
683 return new MultiByteBuff(refCnt
, copy
);
687 * Returns an MBB which is a duplicate version of this MBB. The position, limit and mark of the
688 * new MBB will be independent than that of the original MBB. The content of the new MBB will
689 * start at this MBB's current position The position, limit and mark of the new MBB would be
690 * identical to this MBB in terms of values.
691 * @return a duplicated MBB
694 public MultiByteBuff
duplicate() {
696 ByteBuffer
[] itemsCopy
= new ByteBuffer
[this.items
.length
];
697 for (int i
= 0; i
< this.items
.length
; i
++) {
698 itemsCopy
[i
] = items
[i
].duplicate();
700 return new MultiByteBuff(refCnt
, itemsCopy
, this.itemBeginPos
, this.limit
,
701 this.limitedItemIndex
, this.curItemIndex
, this.markedItemIndex
);
705 * Writes a byte to this MBB at the current position and increments the position
707 * @return this object
710 public MultiByteBuff
put(byte b
) {
712 if (this.curItem
.remaining() == 0) {
713 if (this.curItemIndex
== this.items
.length
- 1) {
714 throw new BufferOverflowException();
717 this.curItem
= this.items
[this.curItemIndex
];
724 * Writes a byte to this MBB at the given index
727 * @return this object
730 public MultiByteBuff
put(int index
, byte b
) {
732 int itemIndex
= getItemIndex(limit
);
733 ByteBuffer item
= items
[itemIndex
];
734 item
.put(index
- itemBeginPos
[itemIndex
], b
);
739 * Copies from a src MBB to this MBB.
740 * @param offset the position in this MBB to which the copy should happen
741 * @param src the src MBB
742 * @param srcOffset the offset in the src MBB from where the elements should be read
743 * @param length the length upto which the copy should happen
746 public MultiByteBuff
put(int offset
, ByteBuff src
, int srcOffset
, int length
) {
748 int destItemIndex
= getItemIndex(offset
);
749 int srcItemIndex
= getItemIndex(srcOffset
);
750 ByteBuffer destItem
= this.items
[destItemIndex
];
751 offset
= offset
- this.itemBeginPos
[destItemIndex
];
753 ByteBuffer srcItem
= getItemByteBuffer(src
, srcItemIndex
);
754 srcOffset
= srcOffset
- this.itemBeginPos
[srcItemIndex
];
755 int toRead
, toWrite
, toMove
;
757 toWrite
= destItem
.limit() - offset
;
758 toRead
= srcItem
.limit() - srcOffset
;
759 toMove
= Math
.min(length
, Math
.min(toRead
, toWrite
));
760 ByteBufferUtils
.copyFromBufferToBuffer(srcItem
, destItem
, srcOffset
, offset
, toMove
);
762 if (length
== 0) break;
763 if (toRead
< toWrite
) {
764 srcItem
= getItemByteBuffer(src
, ++srcItemIndex
);
767 } else if (toRead
> toWrite
) {
768 destItem
= this.items
[++destItemIndex
];
772 // toRead = toWrite case
773 srcItem
= getItemByteBuffer(src
, ++srcItemIndex
);
775 destItem
= this.items
[++destItemIndex
];
782 private static ByteBuffer
getItemByteBuffer(ByteBuff buf
, int index
) {
783 return (buf
instanceof SingleByteBuff
) ? buf
.nioByteBuffers()[0]
784 : ((MultiByteBuff
) buf
).items
[index
];
788 * Writes an int to this MBB at its current position. Also advances the position by size of int
789 * @param val Int value to write
790 * @return this object
793 public MultiByteBuff
putInt(int val
) {
795 if (this.curItem
.remaining() >= Bytes
.SIZEOF_INT
) {
796 this.curItem
.putInt(val
);
799 if (this.curItemIndex
== this.items
.length
- 1) {
800 throw new BufferOverflowException();
802 // During read, we will read as byte by byte for this case. So just write in Big endian
810 private static byte int3(int x
) {
811 return (byte) (x
>> 24);
814 private static byte int2(int x
) {
815 return (byte) (x
>> 16);
818 private static byte int1(int x
) {
819 return (byte) (x
>> 8);
822 private static byte int0(int x
) {
827 * Copies from the given byte[] to this MBB
832 public final MultiByteBuff
put(byte[] src
) {
833 return put(src
, 0, src
.length
);
837 * Copies from the given byte[] to this MBB
839 * @param offset the position in the byte array from which the copy should be done
840 * @param length the length upto which the copy should happen
844 public MultiByteBuff
put(byte[] src
, int offset
, int length
) {
846 if (this.curItem
.remaining() >= length
) {
847 ByteBufferUtils
.copyFromArrayToBuffer(this.curItem
, src
, offset
, length
);
850 int end
= offset
+ length
;
851 for (int i
= offset
; i
< end
; i
++) {
859 * Writes a long to this MBB at its current position. Also advances the position by size of long
860 * @param val Long value to write
861 * @return this object
864 public MultiByteBuff
putLong(long val
) {
866 if (this.curItem
.remaining() >= Bytes
.SIZEOF_LONG
) {
867 this.curItem
.putLong(val
);
870 if (this.curItemIndex
== this.items
.length
- 1) {
871 throw new BufferOverflowException();
873 // During read, we will read as byte by byte for this case. So just write in Big endian
885 private static byte long7(long x
) {
886 return (byte) (x
>> 56);
889 private static byte long6(long x
) {
890 return (byte) (x
>> 48);
893 private static byte long5(long x
) {
894 return (byte) (x
>> 40);
897 private static byte long4(long x
) {
898 return (byte) (x
>> 32);
901 private static byte long3(long x
) {
902 return (byte) (x
>> 24);
905 private static byte long2(long x
) {
906 return (byte) (x
>> 16);
909 private static byte long1(long x
) {
910 return (byte) (x
>> 8);
913 private static byte long0(long x
) {
918 * Jumps the current position of this MBB by specified length.
922 public MultiByteBuff
skip(int length
) {
924 // Get available bytes from this item and remaining from next
927 jump
= this.curItem
.remaining();
928 if (jump
>= length
) {
929 this.curItem
.position(this.curItem
.position() + length
);
932 this.curItem
.position(this.curItem
.position() + jump
);
935 this.curItem
= this.items
[this.curItemIndex
];
941 * Jumps back the current position of this MBB by specified length.
945 public MultiByteBuff
moveBack(int length
) {
947 while (length
!= 0) {
948 if (length
> curItem
.position()) {
949 length
-= curItem
.position();
950 this.curItem
.position(0);
952 this.curItem
= this.items
[curItemIndex
];
954 this.curItem
.position(curItem
.position() - length
);
962 * Returns bytes from current position till length specified, as a single ByteBuffer. When all
963 * these bytes happen to be in a single ByteBuffer, which this object wraps, that ByteBuffer item
964 * as such will be returned. So users are warned not to change the position or limit of this
965 * returned ByteBuffer. The position of the returned byte buffer is at the begin of the required
966 * bytes. When the required bytes happen to span across multiple ByteBuffers, this API will copy
967 * the bytes to a newly created ByteBuffer of required size and return that.
969 * @param length number of bytes required.
970 * @return bytes from current position till length specified, as a single ByteButter.
973 public ByteBuffer
asSubByteBuffer(int length
) {
975 if (this.curItem
.remaining() >= length
) {
979 byte[] dupB
= new byte[length
];
980 int locCurItemIndex
= curItemIndex
;
981 ByteBuffer locCurItem
= curItem
;
983 int toRead
= Math
.min(length
, locCurItem
.remaining());
984 ByteBufferUtils
.copyFromBufferToArray(dupB
, locCurItem
, locCurItem
.position(), offset
,
987 if (length
== 0) break;
989 locCurItem
= this.items
[locCurItemIndex
];
992 return ByteBuffer
.wrap(dupB
);
996 * Returns bytes from given offset till length specified, as a single ByteBuffer. When all these
997 * bytes happen to be in a single ByteBuffer, which this object wraps, that ByteBuffer item as
998 * such will be returned (with offset in this ByteBuffer where the bytes starts). So users are
999 * warned not to change the position or limit of this returned ByteBuffer. When the required bytes
1000 * happen to span across multiple ByteBuffers, this API will copy the bytes to a newly created
1001 * ByteBuffer of required size and return that.
1003 * @param offset the offset in this MBB from where the subBuffer should be created
1004 * @param length the length of the subBuffer
1005 * @param pair a pair that will have the bytes from the current position till length specified, as
1006 * a single ByteBuffer and offset in that Buffer where the bytes starts. The method would
1007 * set the values on the pair that is passed in by the caller
1010 public void asSubByteBuffer(int offset
, int length
, ObjectIntPair
<ByteBuffer
> pair
) {
1012 if (this.itemBeginPos
[this.curItemIndex
] <= offset
) {
1013 int relOffsetInCurItem
= offset
- this.itemBeginPos
[this.curItemIndex
];
1014 if (this.curItem
.limit() - relOffsetInCurItem
>= length
) {
1015 pair
.setFirst(this.curItem
);
1016 pair
.setSecond(relOffsetInCurItem
);
1020 int itemIndex
= getItemIndex(offset
);
1021 ByteBuffer item
= this.items
[itemIndex
];
1022 offset
= offset
- this.itemBeginPos
[itemIndex
];
1023 if (item
.limit() - offset
>= length
) {
1024 pair
.setFirst(item
);
1025 pair
.setSecond(offset
);
1028 byte[] dst
= new byte[length
];
1030 while (length
> 0) {
1031 int toRead
= Math
.min(length
, item
.limit() - offset
);
1032 ByteBufferUtils
.copyFromBufferToArray(dst
, item
, offset
, destOffset
, toRead
);
1034 if (length
== 0) break;
1036 item
= this.items
[itemIndex
];
1037 destOffset
+= toRead
;
1040 pair
.setFirst(ByteBuffer
.wrap(dst
));
1045 * Copies the content from an this MBB to a ByteBuffer
1046 * @param out the ByteBuffer to which the copy has to happen, its position will be advanced.
1047 * @param sourceOffset the offset in the MBB from which the elements has to be copied
1048 * @param length the length in the MBB upto which the elements has to be copied
1051 public void get(ByteBuffer out
, int sourceOffset
, int length
) {
1053 int itemIndex
= getItemIndex(sourceOffset
);
1054 ByteBuffer in
= this.items
[itemIndex
];
1055 sourceOffset
= sourceOffset
- this.itemBeginPos
[itemIndex
];
1056 while (length
> 0) {
1057 int toRead
= Math
.min(in
.limit() - sourceOffset
, length
);
1058 ByteBufferUtils
.copyFromBufferToBuffer(in
, out
, sourceOffset
, toRead
);
1064 in
= this.items
[itemIndex
];
1070 * Copy the content from this MBB to a byte[] based on the given offset and
1074 * the position from where the copy should start
1076 * the length upto which the copy has to be done
1077 * @return byte[] with the copied contents from this MBB.
1080 public byte[] toBytes(int offset
, int length
) {
1082 byte[] output
= new byte[length
];
1083 this.get(offset
, output
, 0, length
);
1087 private int internalRead(ReadableByteChannel channel
, long offset
,
1088 ChannelReader reader
) throws IOException
{
1091 while (buffsIterator
.hasNext()) {
1092 ByteBuffer buffer
= buffsIterator
.next();
1093 int len
= read(channel
, buffer
, offset
, reader
);
1098 if (buffer
.hasRemaining()) {
1106 public int read(ReadableByteChannel channel
) throws IOException
{
1107 return internalRead(channel
, 0, CHANNEL_READER
);
1111 public int read(FileChannel channel
, long offset
) throws IOException
{
1112 return internalRead(channel
, offset
, FILE_READER
);
1116 public int write(FileChannel channel
, long offset
) throws IOException
{
1119 while (buffsIterator
.hasNext()) {
1120 ByteBuffer buffer
= buffsIterator
.next();
1121 while (buffer
.hasRemaining()) {
1122 int len
= channel
.write(buffer
, offset
);
1131 public ByteBuffer
[] nioByteBuffers() {
1137 public boolean equals(Object obj
) {
1138 if (!(obj
instanceof MultiByteBuff
)) return false;
1139 if (this == obj
) return true;
1140 MultiByteBuff that
= (MultiByteBuff
) obj
;
1141 if (this.capacity() != that
.capacity()) return false;
1142 if (ByteBuff
.compareTo(this, this.position(), this.limit(), that
, that
.position(),
1143 that
.limit()) == 0) {
1150 public int hashCode() {
1152 for (ByteBuffer b
: this.items
) {
1153 hash
+= b
.hashCode();
1159 public MultiByteBuff
retain() {