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 throw new IndexOutOfBoundsException();
188 while (elemIndex
>= this.itemBeginPos
[index
]) {
190 if (index
== this.itemBeginPos
.length
) {
191 throw new IndexOutOfBoundsException();
198 * Returns in which sub ByteBuffer, the given element index will be available. In this case we are
199 * sure that the item will be after MBB's current position
201 private int getItemIndexFromCurItemIndex(int elemIndex
) {
202 int index
= this.curItemIndex
;
203 while (elemIndex
>= this.itemBeginPos
[index
]) {
205 if (index
== this.itemBeginPos
.length
) {
206 throw new IndexOutOfBoundsException();
213 * Fetches the int at the given index. Does not change position of the underlying ByteBuffers
215 * @return the int value at the given index
218 public int getInt(int index
) {
220 // Mostly the index specified will land within this current item. Short circuit for that
222 if (this.itemBeginPos
[this.curItemIndex
] <= index
223 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
224 itemIndex
= this.curItemIndex
;
226 itemIndex
= getItemIndex(index
);
228 return getInt(index
, itemIndex
);
232 public int getIntAfterPosition(int offset
) {
234 // Mostly the index specified will land within this current item. Short circuit for that
235 int index
= offset
+ this.position();
237 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
238 itemIndex
= this.curItemIndex
;
240 itemIndex
= getItemIndexFromCurItemIndex(index
);
242 return getInt(index
, itemIndex
);
246 * Fetches the short at the given index. Does not change position of the underlying ByteBuffers
248 * @return the short value at the given index
251 public short getShort(int index
) {
253 // Mostly the index specified will land within this current item. Short circuit for that
255 if (this.itemBeginPos
[this.curItemIndex
] <= index
256 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
257 itemIndex
= this.curItemIndex
;
259 itemIndex
= getItemIndex(index
);
261 ByteBuffer item
= items
[itemIndex
];
262 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
263 if (item
.limit() - offsetInItem
>= Bytes
.SIZEOF_SHORT
) {
264 return ByteBufferUtils
.toShort(item
, offsetInItem
);
266 if (items
.length
- 1 == itemIndex
) {
267 // means cur item is the last one and we wont be able to read a int. Throw exception
268 throw new BufferUnderflowException();
270 ByteBuffer nextItem
= items
[itemIndex
+ 1];
271 // Get available one byte from this item and remaining one from next
273 n
= (short) (n ^
(ByteBufferUtils
.toByte(item
, offsetInItem
) & 0xFF));
274 n
= (short) (n
<< 8);
275 n
= (short) (n ^
(ByteBufferUtils
.toByte(nextItem
, 0) & 0xFF));
280 public short getShortAfterPosition(int offset
) {
282 // Mostly the index specified will land within this current item. Short circuit for that
283 int index
= offset
+ this.position();
285 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
286 itemIndex
= this.curItemIndex
;
288 itemIndex
= getItemIndexFromCurItemIndex(index
);
290 return getShort(index
, itemIndex
);
293 private int getInt(int index
, int itemIndex
) {
294 ByteBuffer item
= items
[itemIndex
];
295 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
296 int remainingLen
= item
.limit() - offsetInItem
;
297 if (remainingLen
>= Bytes
.SIZEOF_INT
) {
298 return ByteBufferUtils
.toInt(item
, offsetInItem
);
300 if (items
.length
- 1 == itemIndex
) {
301 // means cur item is the last one and we wont be able to read a int. Throw exception
302 throw new BufferUnderflowException();
305 for (int i
= 0; i
< Bytes
.SIZEOF_INT
; i
++) {
307 l ^
= get(index
+ i
) & 0xFF;
312 private short getShort(int index
, int itemIndex
) {
313 ByteBuffer item
= items
[itemIndex
];
314 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
315 int remainingLen
= item
.limit() - offsetInItem
;
316 if (remainingLen
>= Bytes
.SIZEOF_SHORT
) {
317 return ByteBufferUtils
.toShort(item
, offsetInItem
);
319 if (items
.length
- 1 == itemIndex
) {
320 // means cur item is the last one and we wont be able to read a short. Throw exception
321 throw new BufferUnderflowException();
323 ByteBuffer nextItem
= items
[itemIndex
+ 1];
324 // Get available bytes from this item and remaining from next
326 for (int i
= offsetInItem
; i
< item
.capacity(); i
++) {
327 l
= (short) (l
<< 8);
328 l
= (short) (l ^
(ByteBufferUtils
.toByte(item
, i
) & 0xFF));
330 for (int i
= 0; i
< Bytes
.SIZEOF_SHORT
- remainingLen
; i
++) {
331 l
= (short) (l
<< 8);
332 l
= (short) (l ^
(ByteBufferUtils
.toByte(nextItem
, i
) & 0xFF));
337 private long getLong(int index
, int itemIndex
) {
338 ByteBuffer item
= items
[itemIndex
];
339 int offsetInItem
= index
- this.itemBeginPos
[itemIndex
];
340 int remainingLen
= item
.limit() - offsetInItem
;
341 if (remainingLen
>= Bytes
.SIZEOF_LONG
) {
342 return ByteBufferUtils
.toLong(item
, offsetInItem
);
344 if (items
.length
- 1 == itemIndex
) {
345 // means cur item is the last one and we wont be able to read a long. Throw exception
346 throw new BufferUnderflowException();
349 for (int i
= 0; i
< Bytes
.SIZEOF_LONG
; i
++) {
351 l ^
= get(index
+ i
) & 0xFF;
357 * Fetches the long at the given index. Does not change position of the underlying ByteBuffers
359 * @return the long value at the given index
362 public long getLong(int index
) {
364 // Mostly the index specified will land within this current item. Short circuit for that
366 if (this.itemBeginPos
[this.curItemIndex
] <= index
367 && this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
368 itemIndex
= this.curItemIndex
;
370 itemIndex
= getItemIndex(index
);
372 return getLong(index
, itemIndex
);
376 public long getLongAfterPosition(int offset
) {
378 // Mostly the index specified will land within this current item. Short circuit for that
379 int index
= offset
+ this.position();
381 if (this.itemBeginPos
[this.curItemIndex
+ 1] > index
) {
382 itemIndex
= this.curItemIndex
;
384 itemIndex
= getItemIndexFromCurItemIndex(index
);
386 return getLong(index
, itemIndex
);
390 * @return this MBB's current position
393 public int position() {
395 return itemBeginPos
[this.curItemIndex
] + this.curItem
.position();
399 * Sets this MBB's position to the given value.
401 * @return this object
404 public MultiByteBuff
position(int position
) {
406 // Short circuit for positioning within the cur item. Mostly that is the case.
407 if (this.itemBeginPos
[this.curItemIndex
] <= position
408 && this.itemBeginPos
[this.curItemIndex
+ 1] > position
) {
409 this.curItem
.position(position
- this.itemBeginPos
[this.curItemIndex
]);
412 int itemIndex
= getItemIndex(position
);
413 // All items from 0 - curItem-1 set position at end.
414 for (int i
= 0; i
< itemIndex
; i
++) {
415 this.items
[i
].position(this.items
[i
].limit());
417 // All items after curItem set position at begin
418 for (int i
= itemIndex
+ 1; i
< this.items
.length
; i
++) {
419 this.items
[i
].position(0);
421 this.curItem
= this.items
[itemIndex
];
422 this.curItem
.position(position
- this.itemBeginPos
[itemIndex
]);
423 this.curItemIndex
= itemIndex
;
428 * Rewinds this MBB and the position is set to 0
429 * @return this object
432 public MultiByteBuff
rewind() {
434 for (int i
= 0; i
< this.items
.length
; i
++) {
435 this.items
[i
].rewind();
437 this.curItemIndex
= 0;
438 this.curItem
= this.items
[this.curItemIndex
];
439 this.markedItemIndex
= -1;
444 * Marks the current position of the MBB
445 * @return this object
448 public MultiByteBuff
mark() {
450 this.markedItemIndex
= this.curItemIndex
;
456 * Similar to {@link ByteBuffer}.reset(), ensures that this MBB
457 * is reset back to last marked position.
461 public MultiByteBuff
reset() {
463 // when the buffer is moved to the next one.. the reset should happen on the previous marked
464 // item and the new one should be taken as the base
465 if (this.markedItemIndex
< 0) throw new InvalidMarkException();
466 ByteBuffer markedItem
= this.items
[this.markedItemIndex
];
468 this.curItem
= markedItem
;
469 // All items after the marked position upto the current item should be reset to 0
470 for (int i
= this.curItemIndex
; i
> this.markedItemIndex
; i
--) {
471 this.items
[i
].position(0);
473 this.curItemIndex
= this.markedItemIndex
;
478 * Returns the number of elements between the current position and the
480 * @return the remaining elements in this MBB
483 public int remaining() {
486 for (int i
= curItemIndex
; i
< items
.length
; i
++) {
487 remain
+= items
[i
].remaining();
493 * Returns true if there are elements between the current position and the limt
494 * @return true if there are elements, false otherwise
497 public final boolean hasRemaining() {
499 return this.curItem
.hasRemaining() || (this.curItemIndex
< this.limitedItemIndex
500 && this.items
[this.curItemIndex
+ 1].hasRemaining());
504 * A relative method that returns byte at the current position. Increments the
505 * current position by the size of a byte.
506 * @return the byte at the current position
511 if (this.curItem
.remaining() == 0) {
512 if (items
.length
- 1 == this.curItemIndex
) {
513 // means cur item is the last one and we wont be able to read a long. Throw exception
514 throw new BufferUnderflowException();
517 this.curItem
= this.items
[this.curItemIndex
];
519 return this.curItem
.get();
523 * Returns the short value at the current position. Also advances the position by the size
526 * @return the short value at the current position
529 public short getShort() {
531 int remaining
= this.curItem
.remaining();
532 if (remaining
>= Bytes
.SIZEOF_SHORT
) {
533 return this.curItem
.getShort();
536 n
= (short) (n ^
(get() & 0xFF));
537 n
= (short) (n
<< 8);
538 n
= (short) (n ^
(get() & 0xFF));
543 * Returns the int value at the current position. Also advances the position by the size of int
545 * @return the int value at the current position
548 public int getInt() {
550 int remaining
= this.curItem
.remaining();
551 if (remaining
>= Bytes
.SIZEOF_INT
) {
552 return this.curItem
.getInt();
555 for (int i
= 0; i
< Bytes
.SIZEOF_INT
; i
++) {
564 * Returns the long value at the current position. Also advances the position by the size of long
566 * @return the long value at the current position
569 public long getLong() {
571 int remaining
= this.curItem
.remaining();
572 if (remaining
>= Bytes
.SIZEOF_LONG
) {
573 return this.curItem
.getLong();
576 for (int i
= 0; i
< Bytes
.SIZEOF_LONG
; i
++) {
584 * Copies the content from this MBB's current position to the byte array and fills it. Also
585 * advances the position of the MBB by the length of the byte[].
589 public void get(byte[] dst
) {
590 get(dst
, 0, dst
.length
);
594 * Copies the specified number of bytes from this MBB's current position to the byte[]'s offset.
595 * Also advances the position of the MBB by the given length.
597 * @param offset within the current array
598 * @param length upto which the bytes to be copied
601 public void get(byte[] dst
, int offset
, int length
) {
604 int toRead
= Math
.min(length
, this.curItem
.remaining());
605 ByteBufferUtils
.copyFromBufferToArray(dst
, this.curItem
, this.curItem
.position(), offset
,
607 this.curItem
.position(this.curItem
.position() + toRead
);
609 if (length
== 0) break;
611 this.curItem
= this.items
[this.curItemIndex
];
617 public void get(int sourceOffset
, byte[] dst
, int offset
, int length
) {
619 int itemIndex
= getItemIndex(sourceOffset
);
620 ByteBuffer item
= this.items
[itemIndex
];
621 sourceOffset
= sourceOffset
- this.itemBeginPos
[itemIndex
];
623 int toRead
= Math
.min((item
.limit() - sourceOffset
), length
);
624 ByteBufferUtils
.copyFromBufferToArray(dst
, item
, sourceOffset
, offset
, toRead
);
626 if (length
== 0) break;
628 item
= this.items
[itemIndex
];
635 * Marks the limit of this MBB.
640 public MultiByteBuff
limit(int limit
) {
643 // Normally the limit will try to limit within the last BB item
644 int limitedIndexBegin
= this.itemBeginPos
[this.limitedItemIndex
];
645 if (limit
>= limitedIndexBegin
&& limit
< this.itemBeginPos
[this.limitedItemIndex
+ 1]) {
646 this.items
[this.limitedItemIndex
].limit(limit
- limitedIndexBegin
);
649 int itemIndex
= getItemIndex(limit
);
650 int beginOffset
= this.itemBeginPos
[itemIndex
];
651 int offsetInItem
= limit
- beginOffset
;
652 ByteBuffer item
= items
[itemIndex
];
653 item
.limit(offsetInItem
);
654 for (int i
= this.limitedItemIndex
; i
< itemIndex
; i
++) {
655 this.items
[i
].limit(this.items
[i
].capacity());
657 this.limitedItemIndex
= itemIndex
;
658 for (int i
= itemIndex
+ 1; i
< this.items
.length
; i
++) {
659 this.items
[i
].limit(this.items
[i
].position());
665 * Returns the limit of this MBB
666 * @return limit of the MBB
674 * Returns an MBB which is a sliced version of this MBB. The position, limit and mark
675 * of the new MBB will be independent than that of the original MBB.
676 * The content of the new MBB will start at this MBB's current position
677 * @return a sliced MBB
680 public MultiByteBuff
slice() {
682 ByteBuffer
[] copy
= new ByteBuffer
[this.limitedItemIndex
- this.curItemIndex
+ 1];
683 for (int i
= curItemIndex
, j
= 0; i
<= this.limitedItemIndex
; i
++, j
++) {
684 copy
[j
] = this.items
[i
].slice();
686 return new MultiByteBuff(refCnt
, copy
);
690 * Returns an MBB which is a duplicate version of this MBB. The position, limit and mark of the
691 * new MBB will be independent than that of the original MBB. The content of the new MBB will
692 * start at this MBB's current position The position, limit and mark of the new MBB would be
693 * identical to this MBB in terms of values.
694 * @return a duplicated MBB
697 public MultiByteBuff
duplicate() {
699 ByteBuffer
[] itemsCopy
= new ByteBuffer
[this.items
.length
];
700 for (int i
= 0; i
< this.items
.length
; i
++) {
701 itemsCopy
[i
] = items
[i
].duplicate();
703 return new MultiByteBuff(refCnt
, itemsCopy
, this.itemBeginPos
, this.limit
,
704 this.limitedItemIndex
, this.curItemIndex
, this.markedItemIndex
);
708 * Writes a byte to this MBB at the current position and increments the position
710 * @return this object
713 public MultiByteBuff
put(byte b
) {
715 if (this.curItem
.remaining() == 0) {
716 if (this.curItemIndex
== this.items
.length
- 1) {
717 throw new BufferOverflowException();
720 this.curItem
= this.items
[this.curItemIndex
];
727 * Writes a byte to this MBB at the given index and won't affect the position of any of the
729 * @return this object
730 * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
731 * {@link MultiByteBuff#limit}
734 public MultiByteBuff
put(int index
, byte b
) {
736 int itemIndex
= getItemIndex(index
);
737 ByteBuffer item
= items
[itemIndex
];
738 item
.put(index
- itemBeginPos
[itemIndex
], b
);
743 * Copies from a src BB to this MBB. This will be absolute positional copying and won't affect the
744 * position of any of the buffers.
745 * @param destOffset the position in this MBB to which the copy should happen
746 * @param src the src MBB
747 * @param srcOffset the offset in the src MBB from where the elements should be read
748 * @param length the length upto which the copy should happen
749 * @throws BufferUnderflowException If there are fewer than length bytes remaining in src
751 * @throws BufferOverflowException If there is insufficient available space in this MBB for length
755 public MultiByteBuff
put(int destOffset
, ByteBuff src
, int srcOffset
, int length
) {
757 int destItemIndex
= getItemIndex(destOffset
);
758 int srcItemIndex
= getItemIndexForByteBuff(src
, srcOffset
, length
);
760 ByteBuffer destItem
= this.items
[destItemIndex
];
761 destOffset
= this.getRelativeOffset(destOffset
, destItemIndex
);
763 ByteBuffer srcItem
= getItemByteBuffer(src
, srcItemIndex
);
764 srcOffset
= getRelativeOffsetForByteBuff(src
, srcOffset
, srcItemIndex
);
767 int toWrite
= destItem
.limit() - destOffset
;
769 throw new BufferOverflowException();
771 int toRead
= srcItem
.limit() - srcOffset
;
773 throw new BufferUnderflowException();
775 int toMove
= Math
.min(length
, Math
.min(toRead
, toWrite
));
776 ByteBufferUtils
.copyFromBufferToBuffer(srcItem
, destItem
, srcOffset
, destOffset
, toMove
);
781 if (toRead
< toWrite
) {
782 if (++srcItemIndex
>= getItemByteBufferCount(src
)) {
783 throw new BufferUnderflowException();
785 srcItem
= getItemByteBuffer(src
, srcItemIndex
);
787 destOffset
+= toMove
;
788 } else if (toRead
> toWrite
) {
789 if (++destItemIndex
>= this.items
.length
) {
790 throw new BufferOverflowException();
792 destItem
= this.items
[destItemIndex
];
796 // toRead = toWrite case
797 if (++srcItemIndex
>= getItemByteBufferCount(src
)) {
798 throw new BufferUnderflowException();
800 srcItem
= getItemByteBuffer(src
, srcItemIndex
);
802 if (++destItemIndex
>= this.items
.length
) {
803 throw new BufferOverflowException();
805 destItem
= this.items
[destItemIndex
];
812 private static ByteBuffer
getItemByteBuffer(ByteBuff buf
, int byteBufferIndex
) {
813 if (buf
instanceof SingleByteBuff
) {
814 if (byteBufferIndex
!= 0) {
815 throw new IndexOutOfBoundsException(
816 "index:[" + byteBufferIndex
+ "],but only index 0 is valid.");
818 return buf
.nioByteBuffers()[0];
820 MultiByteBuff multiByteBuff
= (MultiByteBuff
) buf
;
821 if (byteBufferIndex
< 0 || byteBufferIndex
>= multiByteBuff
.items
.length
) {
822 throw new IndexOutOfBoundsException(
823 "index:[" + byteBufferIndex
+ "],but only index [0-" + multiByteBuff
.items
.length
826 return multiByteBuff
.items
[byteBufferIndex
];
829 private static int getItemIndexForByteBuff(ByteBuff byteBuff
, int offset
, int length
) {
830 if (byteBuff
instanceof SingleByteBuff
) {
831 ByteBuffer byteBuffer
= byteBuff
.nioByteBuffers()[0];
832 if (offset
+ length
> byteBuffer
.limit()) {
833 throw new BufferUnderflowException();
837 MultiByteBuff multiByteBuff
= (MultiByteBuff
) byteBuff
;
838 return multiByteBuff
.getItemIndex(offset
);
841 private static int getRelativeOffsetForByteBuff(ByteBuff byteBuff
, int globalOffset
,
843 if (byteBuff
instanceof SingleByteBuff
) {
844 if (itemIndex
!= 0) {
845 throw new IndexOutOfBoundsException("index:[" + itemIndex
+ "],but only index 0 is valid.");
849 return ((MultiByteBuff
) byteBuff
).getRelativeOffset(globalOffset
, itemIndex
);
852 private int getRelativeOffset(int globalOffset
, int itemIndex
) {
853 if (itemIndex
< 0 || itemIndex
>= this.items
.length
) {
854 throw new IndexOutOfBoundsException(
855 "index:[" + itemIndex
+ "],but only index [0-" + this.items
.length
+ ") is valid.");
857 return globalOffset
- this.itemBeginPos
[itemIndex
];
860 private static int getItemByteBufferCount(ByteBuff buf
) {
861 return (buf
instanceof SingleByteBuff
) ?
1 : ((MultiByteBuff
) buf
).items
.length
;
865 * Writes an int to this MBB at its current position. Also advances the position by size of int
866 * @param val Int value to write
867 * @return this object
870 public MultiByteBuff
putInt(int val
) {
872 if (this.curItem
.remaining() >= Bytes
.SIZEOF_INT
) {
873 this.curItem
.putInt(val
);
876 if (this.curItemIndex
== this.items
.length
- 1) {
877 throw new BufferOverflowException();
879 // During read, we will read as byte by byte for this case. So just write in Big endian
887 private static byte int3(int x
) {
888 return (byte) (x
>> 24);
891 private static byte int2(int x
) {
892 return (byte) (x
>> 16);
895 private static byte int1(int x
) {
896 return (byte) (x
>> 8);
899 private static byte int0(int x
) {
904 * Copies from the given byte[] to this MBB
909 public final MultiByteBuff
put(byte[] src
) {
910 return put(src
, 0, src
.length
);
914 * Copies from the given byte[] to this MBB
916 * @param offset the position in the byte array from which the copy should be done
917 * @param length the length upto which the copy should happen
921 public MultiByteBuff
put(byte[] src
, int offset
, int length
) {
923 if (this.curItem
.remaining() >= length
) {
924 ByteBufferUtils
.copyFromArrayToBuffer(this.curItem
, src
, offset
, length
);
927 int end
= offset
+ length
;
928 for (int i
= offset
; i
< end
; i
++) {
936 * Writes a long to this MBB at its current position. Also advances the position by size of long
937 * @param val Long value to write
938 * @return this object
941 public MultiByteBuff
putLong(long val
) {
943 if (this.curItem
.remaining() >= Bytes
.SIZEOF_LONG
) {
944 this.curItem
.putLong(val
);
947 if (this.curItemIndex
== this.items
.length
- 1) {
948 throw new BufferOverflowException();
950 // During read, we will read as byte by byte for this case. So just write in Big endian
962 private static byte long7(long x
) {
963 return (byte) (x
>> 56);
966 private static byte long6(long x
) {
967 return (byte) (x
>> 48);
970 private static byte long5(long x
) {
971 return (byte) (x
>> 40);
974 private static byte long4(long x
) {
975 return (byte) (x
>> 32);
978 private static byte long3(long x
) {
979 return (byte) (x
>> 24);
982 private static byte long2(long x
) {
983 return (byte) (x
>> 16);
986 private static byte long1(long x
) {
987 return (byte) (x
>> 8);
990 private static byte long0(long x
) {
995 * Jumps the current position of this MBB by specified length.
999 public MultiByteBuff
skip(int length
) {
1001 // Get available bytes from this item and remaining from next
1004 jump
= this.curItem
.remaining();
1005 if (jump
>= length
) {
1006 this.curItem
.position(this.curItem
.position() + length
);
1009 this.curItem
.position(this.curItem
.position() + jump
);
1011 this.curItemIndex
++;
1012 this.curItem
= this.items
[this.curItemIndex
];
1018 * Jumps back the current position of this MBB by specified length.
1022 public MultiByteBuff
moveBack(int length
) {
1024 while (length
!= 0) {
1025 if (length
> curItem
.position()) {
1026 length
-= curItem
.position();
1027 this.curItem
.position(0);
1028 this.curItemIndex
--;
1029 this.curItem
= this.items
[curItemIndex
];
1031 this.curItem
.position(curItem
.position() - length
);
1039 * Returns bytes from current position till length specified, as a single ByteBuffer. When all
1040 * these bytes happen to be in a single ByteBuffer, which this object wraps, that ByteBuffer item
1041 * as such will be returned. So users are warned not to change the position or limit of this
1042 * returned ByteBuffer. The position of the returned byte buffer is at the begin of the required
1043 * bytes. When the required bytes happen to span across multiple ByteBuffers, this API will copy
1044 * the bytes to a newly created ByteBuffer of required size and return that.
1046 * @param length number of bytes required.
1047 * @return bytes from current position till length specified, as a single ByteButter.
1050 public ByteBuffer
asSubByteBuffer(int length
) {
1052 if (this.curItem
.remaining() >= length
) {
1053 return this.curItem
;
1056 byte[] dupB
= new byte[length
];
1057 int locCurItemIndex
= curItemIndex
;
1058 ByteBuffer locCurItem
= curItem
;
1059 while (length
> 0) {
1060 int toRead
= Math
.min(length
, locCurItem
.remaining());
1061 ByteBufferUtils
.copyFromBufferToArray(dupB
, locCurItem
, locCurItem
.position(), offset
,
1064 if (length
== 0) break;
1066 locCurItem
= this.items
[locCurItemIndex
];
1069 return ByteBuffer
.wrap(dupB
);
1073 * Returns bytes from given offset till length specified, as a single ByteBuffer. When all these
1074 * bytes happen to be in a single ByteBuffer, which this object wraps, that ByteBuffer item as
1075 * such will be returned (with offset in this ByteBuffer where the bytes starts). So users are
1076 * warned not to change the position or limit of this returned ByteBuffer. When the required bytes
1077 * happen to span across multiple ByteBuffers, this API will copy the bytes to a newly created
1078 * ByteBuffer of required size and return that.
1080 * @param offset the offset in this MBB from where the subBuffer should be created
1081 * @param length the length of the subBuffer
1082 * @param pair a pair that will have the bytes from the current position till length specified, as
1083 * a single ByteBuffer and offset in that Buffer where the bytes starts. The method would
1084 * set the values on the pair that is passed in by the caller
1087 public void asSubByteBuffer(int offset
, int length
, ObjectIntPair
<ByteBuffer
> pair
) {
1089 if (this.itemBeginPos
[this.curItemIndex
] <= offset
) {
1090 int relOffsetInCurItem
= offset
- this.itemBeginPos
[this.curItemIndex
];
1091 if (this.curItem
.limit() - relOffsetInCurItem
>= length
) {
1092 pair
.setFirst(this.curItem
);
1093 pair
.setSecond(relOffsetInCurItem
);
1097 int itemIndex
= getItemIndex(offset
);
1098 ByteBuffer item
= this.items
[itemIndex
];
1099 offset
= offset
- this.itemBeginPos
[itemIndex
];
1100 if (item
.limit() - offset
>= length
) {
1101 pair
.setFirst(item
);
1102 pair
.setSecond(offset
);
1105 byte[] dst
= new byte[length
];
1107 while (length
> 0) {
1108 int toRead
= Math
.min(length
, item
.limit() - offset
);
1109 ByteBufferUtils
.copyFromBufferToArray(dst
, item
, offset
, destOffset
, toRead
);
1111 if (length
== 0) break;
1113 item
= this.items
[itemIndex
];
1114 destOffset
+= toRead
;
1117 pair
.setFirst(ByteBuffer
.wrap(dst
));
1122 * Copies the content from an this MBB to a ByteBuffer
1123 * @param out the ByteBuffer to which the copy has to happen, its position will be advanced.
1124 * @param sourceOffset the offset in the MBB from which the elements has to be copied
1125 * @param length the length in the MBB upto which the elements has to be copied
1128 public void get(ByteBuffer out
, int sourceOffset
, int length
) {
1130 int itemIndex
= getItemIndex(sourceOffset
);
1131 ByteBuffer in
= this.items
[itemIndex
];
1132 sourceOffset
= sourceOffset
- this.itemBeginPos
[itemIndex
];
1133 while (length
> 0) {
1134 int toRead
= Math
.min(in
.limit() - sourceOffset
, length
);
1135 ByteBufferUtils
.copyFromBufferToBuffer(in
, out
, sourceOffset
, toRead
);
1141 in
= this.items
[itemIndex
];
1147 * Copy the content from this MBB to a byte[] based on the given offset and
1151 * the position from where the copy should start
1153 * the length upto which the copy has to be done
1154 * @return byte[] with the copied contents from this MBB.
1157 public byte[] toBytes(int offset
, int length
) {
1159 byte[] output
= new byte[length
];
1160 this.get(offset
, output
, 0, length
);
1164 private int internalRead(ReadableByteChannel channel
, long offset
,
1165 ChannelReader reader
) throws IOException
{
1168 while (buffsIterator
.hasNext()) {
1169 ByteBuffer buffer
= buffsIterator
.next();
1170 int len
= read(channel
, buffer
, offset
, reader
);
1175 if (buffer
.hasRemaining()) {
1183 public int read(ReadableByteChannel channel
) throws IOException
{
1184 return internalRead(channel
, 0, CHANNEL_READER
);
1188 public int read(FileChannel channel
, long offset
) throws IOException
{
1189 return internalRead(channel
, offset
, FILE_READER
);
1193 public int write(FileChannel channel
, long offset
) throws IOException
{
1196 while (buffsIterator
.hasNext()) {
1197 ByteBuffer buffer
= buffsIterator
.next();
1198 while (buffer
.hasRemaining()) {
1199 int len
= channel
.write(buffer
, offset
);
1208 public ByteBuffer
[] nioByteBuffers() {
1214 public boolean equals(Object obj
) {
1215 if (!(obj
instanceof MultiByteBuff
)) return false;
1216 if (this == obj
) return true;
1217 MultiByteBuff that
= (MultiByteBuff
) obj
;
1218 if (this.capacity() != that
.capacity()) return false;
1219 if (ByteBuff
.compareTo(this, this.position(), this.limit(), that
, that
.position(),
1220 that
.limit()) == 0) {
1227 public int hashCode() {
1229 for (ByteBuffer b
: this.items
) {
1230 hash
+= b
.hashCode();
1236 public MultiByteBuff
retain() {