com.datastax.driver.$internal.io.netty.buffer.CompositeByteBuf Maven / Gradle / Ivy
Show all versions of cassandra-driver Show documentation
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.datastax.driver.$internal.io.netty.buffer;
import com.datastax.driver.$internal.io.netty.util.internal.EmptyArrays;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import static com.datastax.driver.$internal.io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* A virtual buffer which shows multiple buffers as a single merged buffer. It is recommended to use
* {@link ByteBufAllocator#compositeBuffer()} or {@link Unpooled#wrappedBuffer(ByteBuf...)} instead of calling the
* constructor explicitly.
*/
public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable {
private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer();
private static final Iterator EMPTY_ITERATOR = Collections.emptyList().iterator();
private final ByteBufAllocator alloc;
private final boolean direct;
private final ComponentList components;
private final int maxNumComponents;
private boolean freed;
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
if (alloc == null) {
throw new NullPointerException("alloc");
}
this.alloc = alloc;
this.direct = direct;
this.maxNumComponents = maxNumComponents;
components = newList(maxNumComponents);
}
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) {
this(alloc, direct, maxNumComponents, buffers, 0, buffers.length);
}
CompositeByteBuf(
ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf[] buffers, int offset, int len) {
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
if (alloc == null) {
throw new NullPointerException("alloc");
}
if (maxNumComponents < 2) {
throw new IllegalArgumentException(
"maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
}
this.alloc = alloc;
this.direct = direct;
this.maxNumComponents = maxNumComponents;
components = newList(maxNumComponents);
addComponents0(false, 0, buffers, offset, len);
consolidateIfNeeded();
setIndex(0, capacity());
}
public CompositeByteBuf(
ByteBufAllocator alloc, boolean direct, int maxNumComponents, Iterable buffers) {
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
if (alloc == null) {
throw new NullPointerException("alloc");
}
if (maxNumComponents < 2) {
throw new IllegalArgumentException(
"maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
}
this.alloc = alloc;
this.direct = direct;
this.maxNumComponents = maxNumComponents;
components = newList(maxNumComponents);
addComponents0(false, 0, buffers);
consolidateIfNeeded();
setIndex(0, capacity());
}
private static ComponentList newList(int maxNumComponents) {
return new ComponentList(Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents));
}
// Special constructor used by WrappedCompositeByteBuf
CompositeByteBuf(ByteBufAllocator alloc) {
super(Integer.MAX_VALUE);
this.alloc = alloc;
direct = false;
maxNumComponents = 0;
components = null;
}
/**
* Add the given {@link ByteBuf}.
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponent(boolean, ByteBuf)}.
*
* {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
* @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(ByteBuf buffer) {
return addComponent(false, buffer);
}
/**
* Add the given {@link ByteBuf}s.
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponents(boolean, ByteBuf[])}.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
* ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(ByteBuf... buffers) {
return addComponents(false, buffers);
}
/**
* Add the given {@link ByteBuf}s.
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponents(boolean, Iterable)}.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
* ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(Iterable buffers) {
return addComponents(false, buffers);
}
/**
* Add the given {@link ByteBuf} on the specific index.
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponent(boolean, int, ByteBuf)}.
*
* {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
* @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) {
return addComponent(false, cIndex, buffer);
}
/**
* Add the given {@link ByteBuf} and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
* {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
* @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) {
checkNotNull(buffer, "buffer");
addComponent0(increaseWriterIndex, components.size(), buffer);
consolidateIfNeeded();
return this;
}
/**
* Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
* ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) {
addComponents0(increaseWriterIndex, components.size(), buffers, 0, buffers.length);
consolidateIfNeeded();
return this;
}
/**
* Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
* ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable buffers) {
addComponents0(increaseWriterIndex, components.size(), buffers);
consolidateIfNeeded();
return this;
}
/**
* Add the given {@link ByteBuf} on the specific index and increase the {@code writerIndex}
* if {@code increaseWriterIndex} is {@code true}.
*
* {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
* @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(boolean increaseWriterIndex, int cIndex, ByteBuf buffer) {
checkNotNull(buffer, "buffer");
addComponent0(increaseWriterIndex, cIndex, buffer);
consolidateIfNeeded();
return this;
}
/**
* Precondition is that {@code buffer != null}.
*/
private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffer) {
assert buffer != null;
boolean wasAdded = false;
try {
checkComponentIndex(cIndex);
int readableBytes = buffer.readableBytes();
// No need to consolidate - just add a component to the list.
@SuppressWarnings("deprecation")
Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice());
if (cIndex == components.size()) {
wasAdded = components.add(c);
if (cIndex == 0) {
c.endOffset = readableBytes;
} else {
Component prev = components.get(cIndex - 1);
c.offset = prev.endOffset;
c.endOffset = c.offset + readableBytes;
}
} else {
components.add(cIndex, c);
wasAdded = true;
if (readableBytes != 0) {
updateComponentOffsets(cIndex);
}
}
if (increaseWriterIndex) {
writerIndex(writerIndex() + buffer.readableBytes());
}
return cIndex;
} finally {
if (!wasAdded) {
buffer.release();
}
}
}
/**
* Add the given {@link ByteBuf}s on the specific index
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased you need to handle it by your own.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added. {@link ByteBuf#release()} ownership of all
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
* ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) {
addComponents0(false, cIndex, buffers, 0, buffers.length);
consolidateIfNeeded();
return this;
}
private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] buffers, int offset, int len) {
checkNotNull(buffers, "buffers");
int i = offset;
try {
checkComponentIndex(cIndex);
// No need for consolidation
while (i < len) {
// Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
// will release if an exception occurs, and we also release in the finally block here).
ByteBuf b = buffers[i++];
if (b == null) {
break;
}
cIndex = addComponent0(increaseWriterIndex, cIndex, b) + 1;
int size = components.size();
if (cIndex > size) {
cIndex = size;
}
}
return cIndex;
} finally {
for (; i < len; ++i) {
ByteBuf b = buffers[i];
if (b != null) {
try {
b.release();
} catch (Throwable ignored) {
// ignore
}
}
}
}
}
/**
* Add the given {@link ByteBuf}s on the specific index
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased you need to handle it by your own.
*
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
* {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all
* {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(int cIndex, Iterable buffers) {
addComponents0(false, cIndex, buffers);
consolidateIfNeeded();
return this;
}
private int addComponents0(boolean increaseIndex, int cIndex, Iterable buffers) {
if (buffers instanceof ByteBuf) {
// If buffers also implements ByteBuf (e.g. CompositeByteBuf), it has to go to addComponent(ByteBuf).
return addComponent0(increaseIndex, cIndex, (ByteBuf) buffers);
}
checkNotNull(buffers, "buffers");
if (!(buffers instanceof Collection)) {
List list = new ArrayList();
try {
for (ByteBuf b: buffers) {
list.add(b);
}
buffers = list;
} finally {
if (buffers != list) {
for (ByteBuf b: buffers) {
if (b != null) {
try {
b.release();
} catch (Throwable ignored) {
// ignore
}
}
}
}
}
}
Collection col = (Collection) buffers;
return addComponents0(increaseIndex, cIndex, col.toArray(new ByteBuf[col.size()]), 0 , col.size());
}
/**
* This should only be called as last operation from a method as this may adjust the underlying
* array of components and so affect the index etc.
*/
private void consolidateIfNeeded() {
// Consolidate if the number of components will exceed the allowed maximum by the current
// operation.
final int numComponents = components.size();
if (numComponents > maxNumComponents) {
final int capacity = components.get(numComponents - 1).endOffset;
ByteBuf consolidated = allocBuffer(capacity);
// We're not using foreach to avoid creating an iterator.
for (int i = 0; i < numComponents; i ++) {
Component c = components.get(i);
ByteBuf b = c.buf;
consolidated.writeBytes(b);
c.freeIfNecessary();
}
Component c = new Component(consolidated);
c.endOffset = c.length;
components.clear();
components.add(c);
}
}
private void checkComponentIndex(int cIndex) {
ensureAccessible();
if (cIndex < 0 || cIndex > components.size()) {
throw new IndexOutOfBoundsException(String.format(
"cIndex: %d (expected: >= 0 && <= numComponents(%d))",
cIndex, components.size()));
}
}
private void checkComponentIndex(int cIndex, int numComponents) {
ensureAccessible();
if (cIndex < 0 || cIndex + numComponents > components.size()) {
throw new IndexOutOfBoundsException(String.format(
"cIndex: %d, numComponents: %d " +
"(expected: cIndex >= 0 && cIndex + numComponents <= totalNumComponents(%d))",
cIndex, numComponents, components.size()));
}
}
private void updateComponentOffsets(int cIndex) {
int size = components.size();
if (size <= cIndex) {
return;
}
Component c = components.get(cIndex);
if (cIndex == 0) {
c.offset = 0;
c.endOffset = c.length;
cIndex ++;
}
for (int i = cIndex; i < size; i ++) {
Component prev = components.get(i - 1);
Component cur = components.get(i);
cur.offset = prev.endOffset;
cur.endOffset = cur.offset + cur.length;
}
}
/**
* Remove the {@link ByteBuf} from the given index.
*
* @param cIndex the index on from which the {@link ByteBuf} will be remove
*/
public CompositeByteBuf removeComponent(int cIndex) {
checkComponentIndex(cIndex);
Component comp = components.remove(cIndex);
comp.freeIfNecessary();
if (comp.length > 0) {
// Only need to call updateComponentOffsets if the length was > 0
updateComponentOffsets(cIndex);
}
return this;
}
/**
* Remove the number of {@link ByteBuf}s starting from the given index.
*
* @param cIndex the index on which the {@link ByteBuf}s will be started to removed
* @param numComponents the number of components to remove
*/
public CompositeByteBuf removeComponents(int cIndex, int numComponents) {
checkComponentIndex(cIndex, numComponents);
if (numComponents == 0) {
return this;
}
int endIndex = cIndex + numComponents;
boolean needsUpdate = false;
for (int i = cIndex; i < endIndex; ++i) {
Component c = components.get(i);
if (c.length > 0) {
needsUpdate = true;
}
c.freeIfNecessary();
}
components.removeRange(cIndex, endIndex);
if (needsUpdate) {
// Only need to call updateComponentOffsets if the length was > 0
updateComponentOffsets(cIndex);
}
return this;
}
@Override
public Iterator iterator() {
ensureAccessible();
if (components.isEmpty()) {
return EMPTY_ITERATOR;
}
return new CompositeByteBufIterator();
}
/**
* Same with {@link #slice(int, int)} except that this method returns a list.
*/
public List decompose(int offset, int length) {
checkIndex(offset, length);
if (length == 0) {
return Collections.emptyList();
}
int componentId = toComponentIndex(offset);
List slice = new ArrayList(components.size());
// The first component
Component firstC = components.get(componentId);
ByteBuf first = firstC.buf.duplicate();
first.readerIndex(offset - firstC.offset);
ByteBuf buf = first;
int bytesToSlice = length;
do {
int readableBytes = buf.readableBytes();
if (bytesToSlice <= readableBytes) {
// Last component
buf.writerIndex(buf.readerIndex() + bytesToSlice);
slice.add(buf);
break;
} else {
// Not the last component
slice.add(buf);
bytesToSlice -= readableBytes;
componentId ++;
// Fetch the next component.
buf = components.get(componentId).buf.duplicate();
}
} while (bytesToSlice > 0);
// Slice all components because only readable bytes are interesting.
for (int i = 0; i < slice.size(); i ++) {
slice.set(i, slice.get(i).slice());
}
return slice;
}
@Override
public boolean isDirect() {
int size = components.size();
if (size == 0) {
return false;
}
for (int i = 0; i < size; i++) {
if (!components.get(i).buf.isDirect()) {
return false;
}
}
return true;
}
@Override
public boolean hasArray() {
switch (components.size()) {
case 0:
return true;
case 1:
return components.get(0).buf.hasArray();
default:
return false;
}
}
@Override
public byte[] array() {
switch (components.size()) {
case 0:
return EmptyArrays.EMPTY_BYTES;
case 1:
return components.get(0).buf.array();
default:
throw new UnsupportedOperationException();
}
}
@Override
public int arrayOffset() {
switch (components.size()) {
case 0:
return 0;
case 1:
return components.get(0).buf.arrayOffset();
default:
throw new UnsupportedOperationException();
}
}
@Override
public boolean hasMemoryAddress() {
switch (components.size()) {
case 0:
return Unpooled.EMPTY_BUFFER.hasMemoryAddress();
case 1:
return components.get(0).buf.hasMemoryAddress();
default:
return false;
}
}
@Override
public long memoryAddress() {
switch (components.size()) {
case 0:
return Unpooled.EMPTY_BUFFER.memoryAddress();
case 1:
return components.get(0).buf.memoryAddress();
default:
throw new UnsupportedOperationException();
}
}
@Override
public int capacity() {
final int numComponents = components.size();
if (numComponents == 0) {
return 0;
}
return components.get(numComponents - 1).endOffset;
}
@Override
public CompositeByteBuf capacity(int newCapacity) {
checkNewCapacity(newCapacity);
int oldCapacity = capacity();
if (newCapacity > oldCapacity) {
final int paddingLength = newCapacity - oldCapacity;
ByteBuf padding;
int nComponents = components.size();
if (nComponents < maxNumComponents) {
padding = allocBuffer(paddingLength);
padding.setIndex(0, paddingLength);
addComponent0(false, components.size(), padding);
} else {
padding = allocBuffer(paddingLength);
padding.setIndex(0, paddingLength);
// FIXME: No need to create a padding buffer and consolidate.
// Just create a big single buffer and put the current content there.
addComponent0(false, components.size(), padding);
consolidateIfNeeded();
}
} else if (newCapacity < oldCapacity) {
int bytesToTrim = oldCapacity - newCapacity;
for (ListIterator i = components.listIterator(components.size()); i.hasPrevious();) {
Component c = i.previous();
if (bytesToTrim >= c.length) {
bytesToTrim -= c.length;
i.remove();
continue;
}
// Replace the last component with the trimmed slice.
Component newC = new Component(c.buf.slice(0, c.length - bytesToTrim));
newC.offset = c.offset;
newC.endOffset = newC.offset + newC.length;
i.set(newC);
break;
}
if (readerIndex() > newCapacity) {
setIndex(newCapacity, newCapacity);
} else if (writerIndex() > newCapacity) {
writerIndex(newCapacity);
}
}
return this;
}
@Override
public ByteBufAllocator alloc() {
return alloc;
}
@Override
public ByteOrder order() {
return ByteOrder.BIG_ENDIAN;
}
/**
* Return the current number of {@link ByteBuf}'s that are composed in this instance
*/
public int numComponents() {
return components.size();
}
/**
* Return the max number of {@link ByteBuf}'s that are composed in this instance
*/
public int maxNumComponents() {
return maxNumComponents;
}
/**
* Return the index for the given offset
*/
public int toComponentIndex(int offset) {
checkIndex(offset);
for (int low = 0, high = components.size(); low <= high;) {
int mid = low + high >>> 1;
Component c = components.get(mid);
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
high = mid - 1;
} else {
return mid;
}
}
throw new Error("should not reach here");
}
public int toByteIndex(int cIndex) {
checkComponentIndex(cIndex);
return components.get(cIndex).offset;
}
@Override
public byte getByte(int index) {
return _getByte(index);
}
@Override
protected byte _getByte(int index) {
Component c = findComponent(index);
return c.buf.getByte(index - c.offset);
}
@Override
protected short _getShort(int index) {
Component c = findComponent(index);
if (index + 2 <= c.endOffset) {
return c.buf.getShort(index - c.offset);
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (short) ((_getByte(index) & 0xff) << 8 | _getByte(index + 1) & 0xff);
} else {
return (short) (_getByte(index) & 0xff | (_getByte(index + 1) & 0xff) << 8);
}
}
@Override
protected int _getUnsignedMedium(int index) {
Component c = findComponent(index);
if (index + 3 <= c.endOffset) {
return c.buf.getUnsignedMedium(index - c.offset);
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getShort(index) & 0xffff) << 8 | _getByte(index + 2) & 0xff;
} else {
return _getShort(index) & 0xFFFF | (_getByte(index + 2) & 0xFF) << 16;
}
}
@Override
protected int _getInt(int index) {
Component c = findComponent(index);
if (index + 4 <= c.endOffset) {
return c.buf.getInt(index - c.offset);
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getShort(index) & 0xffff) << 16 | _getShort(index + 2) & 0xffff;
} else {
return _getShort(index) & 0xFFFF | (_getShort(index + 2) & 0xFFFF) << 16;
}
}
@Override
protected long _getLong(int index) {
Component c = findComponent(index);
if (index + 8 <= c.endOffset) {
return c.buf.getLong(index - c.offset);
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getInt(index) & 0xffffffffL) << 32 | _getInt(index + 4) & 0xffffffffL;
} else {
return _getInt(index) & 0xFFFFFFFFL | (_getInt(index + 4) & 0xFFFFFFFFL) << 32;
}
}
@Override
public CompositeByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
checkDstIndex(index, length, dstIndex, dst.length);
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.getBytes(index - adjustment, dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
i ++;
}
return this;
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuffer dst) {
int limit = dst.limit();
int length = dst.remaining();
checkIndex(index, length);
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
try {
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
dst.limit(dst.position() + localLength);
s.getBytes(index - adjustment, dst);
index += localLength;
length -= localLength;
i ++;
}
} finally {
dst.limit(limit);
}
return this;
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
checkDstIndex(index, length, dstIndex, dst.capacity());
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.getBytes(index - adjustment, dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
i ++;
}
return this;
}
@Override
public int getBytes(int index, GatheringByteChannel out, int length)
throws IOException {
int count = nioBufferCount();
if (count == 1) {
return out.write(internalNioBuffer(index, length));
} else {
long writtenBytes = out.write(nioBuffers(index, length));
if (writtenBytes > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else {
return (int) writtenBytes;
}
}
}
@Override
public CompositeByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
checkIndex(index, length);
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.getBytes(index - adjustment, out, localLength);
index += localLength;
length -= localLength;
i ++;
}
return this;
}
@Override
public CompositeByteBuf setByte(int index, int value) {
Component c = findComponent(index);
c.buf.setByte(index - c.offset, value);
return this;
}
@Override
protected void _setByte(int index, int value) {
setByte(index, value);
}
@Override
public CompositeByteBuf setShort(int index, int value) {
return (CompositeByteBuf) super.setShort(index, value);
}
@Override
protected void _setShort(int index, int value) {
Component c = findComponent(index);
if (index + 2 <= c.endOffset) {
c.buf.setShort(index - c.offset, value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setByte(index, (byte) (value >>> 8));
_setByte(index + 1, (byte) value);
} else {
_setByte(index, (byte) value);
_setByte(index + 1, (byte) (value >>> 8));
}
}
@Override
public CompositeByteBuf setMedium(int index, int value) {
return (CompositeByteBuf) super.setMedium(index, value);
}
@Override
protected void _setMedium(int index, int value) {
Component c = findComponent(index);
if (index + 3 <= c.endOffset) {
c.buf.setMedium(index - c.offset, value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShort(index, (short) (value >> 8));
_setByte(index + 2, (byte) value);
} else {
_setShort(index, (short) value);
_setByte(index + 2, (byte) (value >>> 16));
}
}
@Override
public CompositeByteBuf setInt(int index, int value) {
return (CompositeByteBuf) super.setInt(index, value);
}
@Override
protected void _setInt(int index, int value) {
Component c = findComponent(index);
if (index + 4 <= c.endOffset) {
c.buf.setInt(index - c.offset, value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShort(index, (short) (value >>> 16));
_setShort(index + 2, (short) value);
} else {
_setShort(index, (short) value);
_setShort(index + 2, (short) (value >>> 16));
}
}
@Override
public CompositeByteBuf setLong(int index, long value) {
return (CompositeByteBuf) super.setLong(index, value);
}
@Override
protected void _setLong(int index, long value) {
Component c = findComponent(index);
if (index + 8 <= c.endOffset) {
c.buf.setLong(index - c.offset, value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setInt(index, (int) (value >>> 32));
_setInt(index + 4, (int) value);
} else {
_setInt(index, (int) value);
_setInt(index + 4, (int) (value >>> 32));
}
}
@Override
public CompositeByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
checkSrcIndex(index, length, srcIndex, src.length);
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.setBytes(index - adjustment, src, srcIndex, localLength);
index += localLength;
srcIndex += localLength;
length -= localLength;
i ++;
}
return this;
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuffer src) {
int limit = src.limit();
int length = src.remaining();
checkIndex(index, length);
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
try {
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
src.limit(src.position() + localLength);
s.setBytes(index - adjustment, src);
index += localLength;
length -= localLength;
i ++;
}
} finally {
src.limit(limit);
}
return this;
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
checkSrcIndex(index, length, srcIndex, src.capacity());
if (length == 0) {
return this;
}
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.setBytes(index - adjustment, src, srcIndex, localLength);
index += localLength;
srcIndex += localLength;
length -= localLength;
i ++;
}
return this;
}
@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
checkIndex(index, length);
if (length == 0) {
return in.read(EmptyArrays.EMPTY_BYTES);
}
int i = toComponentIndex(index);
int readBytes = 0;
do {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
int localReadBytes = s.setBytes(index - adjustment, in, localLength);
if (localReadBytes < 0) {
if (readBytes == 0) {
return -1;
} else {
break;
}
}
if (localReadBytes == localLength) {
index += localLength;
length -= localLength;
readBytes += localLength;
i ++;
} else {
index += localReadBytes;
length -= localReadBytes;
readBytes += localReadBytes;
}
} while (length > 0);
return readBytes;
}
@Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
checkIndex(index, length);
if (length == 0) {
return in.read(EMPTY_NIO_BUFFER);
}
int i = toComponentIndex(index);
int readBytes = 0;
do {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
int localReadBytes = s.setBytes(index - adjustment, in, localLength);
if (localReadBytes == 0) {
break;
}
if (localReadBytes < 0) {
if (readBytes == 0) {
return -1;
} else {
break;
}
}
if (localReadBytes == localLength) {
index += localLength;
length -= localLength;
readBytes += localLength;
i ++;
} else {
index += localReadBytes;
length -= localReadBytes;
readBytes += localReadBytes;
}
} while (length > 0);
return readBytes;
}
@Override
public ByteBuf copy(int index, int length) {
checkIndex(index, length);
ByteBuf dst = allocBuffer(length);
if (length != 0) {
copyTo(index, length, toComponentIndex(index), dst);
}
return dst;
}
private void copyTo(int index, int length, int componentId, ByteBuf dst) {
int dstIndex = 0;
int i = componentId;
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
s.getBytes(index - adjustment, dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
i ++;
}
dst.writerIndex(dst.capacity());
}
/**
* Return the {@link ByteBuf} on the specified index
*
* @param cIndex the index for which the {@link ByteBuf} should be returned
* @return buf the {@link ByteBuf} on the specified index
*/
public ByteBuf component(int cIndex) {
return internalComponent(cIndex).duplicate();
}
/**
* Return the {@link ByteBuf} on the specified index
*
* @param offset the offset for which the {@link ByteBuf} should be returned
* @return the {@link ByteBuf} on the specified index
*/
public ByteBuf componentAtOffset(int offset) {
return internalComponentAtOffset(offset).duplicate();
}
/**
* Return the internal {@link ByteBuf} on the specified index. Note that updating the indexes of the returned
* buffer will lead to an undefined behavior of this buffer.
*
* @param cIndex the index for which the {@link ByteBuf} should be returned
*/
public ByteBuf internalComponent(int cIndex) {
checkComponentIndex(cIndex);
return components.get(cIndex).buf;
}
/**
* Return the internal {@link ByteBuf} on the specified offset. Note that updating the indexes of the returned
* buffer will lead to an undefined behavior of this buffer.
*
* @param offset the offset for which the {@link ByteBuf} should be returned
*/
public ByteBuf internalComponentAtOffset(int offset) {
return findComponent(offset).buf;
}
private Component findComponent(int offset) {
checkIndex(offset);
for (int low = 0, high = components.size(); low <= high;) {
int mid = low + high >>> 1;
Component c = components.get(mid);
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
high = mid - 1;
} else {
assert c.length != 0;
return c;
}
}
throw new Error("should not reach here");
}
@Override
public int nioBufferCount() {
switch (components.size()) {
case 0:
return 1;
case 1:
return components.get(0).buf.nioBufferCount();
default:
int count = 0;
int componentsCount = components.size();
for (int i = 0; i < componentsCount; i++) {
Component c = components.get(i);
count += c.buf.nioBufferCount();
}
return count;
}
}
@Override
public ByteBuffer internalNioBuffer(int index, int length) {
switch (components.size()) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
return components.get(0).buf.internalNioBuffer(index, length);
default:
throw new UnsupportedOperationException();
}
}
@Override
public ByteBuffer nioBuffer(int index, int length) {
checkIndex(index, length);
switch (components.size()) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
ByteBuf buf = components.get(0).buf;
if (buf.nioBufferCount() == 1) {
return components.get(0).buf.nioBuffer(index, length);
}
}
ByteBuffer merged = ByteBuffer.allocate(length).order(order());
ByteBuffer[] buffers = nioBuffers(index, length);
for (ByteBuffer buf: buffers) {
merged.put(buf);
}
merged.flip();
return merged;
}
@Override
public ByteBuffer[] nioBuffers(int index, int length) {
checkIndex(index, length);
if (length == 0) {
return new ByteBuffer[] { EMPTY_NIO_BUFFER };
}
List buffers = new ArrayList(components.size());
int i = toComponentIndex(index);
while (length > 0) {
Component c = components.get(i);
ByteBuf s = c.buf;
int adjustment = c.offset;
int localLength = Math.min(length, s.capacity() - (index - adjustment));
switch (s.nioBufferCount()) {
case 0:
throw new UnsupportedOperationException();
case 1:
buffers.add(s.nioBuffer(index - adjustment, localLength));
break;
default:
Collections.addAll(buffers, s.nioBuffers(index - adjustment, localLength));
}
index += localLength;
length -= localLength;
i ++;
}
return buffers.toArray(new ByteBuffer[buffers.size()]);
}
/**
* Consolidate the composed {@link ByteBuf}s
*/
public CompositeByteBuf consolidate() {
ensureAccessible();
final int numComponents = numComponents();
if (numComponents <= 1) {
return this;
}
final Component last = components.get(numComponents - 1);
final int capacity = last.endOffset;
final ByteBuf consolidated = allocBuffer(capacity);
for (int i = 0; i < numComponents; i ++) {
Component c = components.get(i);
ByteBuf b = c.buf;
consolidated.writeBytes(b);
c.freeIfNecessary();
}
components.clear();
components.add(new Component(consolidated));
updateComponentOffsets(0);
return this;
}
/**
* Consolidate the composed {@link ByteBuf}s
*
* @param cIndex the index on which to start to compose
* @param numComponents the number of components to compose
*/
public CompositeByteBuf consolidate(int cIndex, int numComponents) {
checkComponentIndex(cIndex, numComponents);
if (numComponents <= 1) {
return this;
}
final int endCIndex = cIndex + numComponents;
final Component last = components.get(endCIndex - 1);
final int capacity = last.endOffset - components.get(cIndex).offset;
final ByteBuf consolidated = allocBuffer(capacity);
for (int i = cIndex; i < endCIndex; i ++) {
Component c = components.get(i);
ByteBuf b = c.buf;
consolidated.writeBytes(b);
c.freeIfNecessary();
}
components.removeRange(cIndex + 1, endCIndex);
components.set(cIndex, new Component(consolidated));
updateComponentOffsets(cIndex);
return this;
}
/**
* Discard all {@link ByteBuf}s which are read.
*/
public CompositeByteBuf discardReadComponents() {
ensureAccessible();
final int readerIndex = readerIndex();
if (readerIndex == 0) {
return this;
}
// Discard everything if (readerIndex = writerIndex = capacity).
int writerIndex = writerIndex();
if (readerIndex == writerIndex && writerIndex == capacity()) {
int size = components.size();
for (int i = 0; i < size; i++) {
components.get(i).freeIfNecessary();
}
components.clear();
setIndex(0, 0);
adjustMarkers(readerIndex);
return this;
}
// Remove read components.
int firstComponentId = toComponentIndex(readerIndex);
for (int i = 0; i < firstComponentId; i ++) {
components.get(i).freeIfNecessary();
}
components.removeRange(0, firstComponentId);
// Update indexes and markers.
Component first = components.get(0);
int offset = first.offset;
updateComponentOffsets(0);
setIndex(readerIndex - offset, writerIndex - offset);
adjustMarkers(offset);
return this;
}
@Override
public CompositeByteBuf discardReadBytes() {
ensureAccessible();
final int readerIndex = readerIndex();
if (readerIndex == 0) {
return this;
}
// Discard everything if (readerIndex = writerIndex = capacity).
int writerIndex = writerIndex();
if (readerIndex == writerIndex && writerIndex == capacity()) {
int size = components.size();
for (int i = 0; i < size; i++) {
components.get(i).freeIfNecessary();
}
components.clear();
setIndex(0, 0);
adjustMarkers(readerIndex);
return this;
}
// Remove read components.
int firstComponentId = toComponentIndex(readerIndex);
for (int i = 0; i < firstComponentId; i ++) {
components.get(i).freeIfNecessary();
}
// Remove or replace the first readable component with a new slice.
Component c = components.get(firstComponentId);
int adjustment = readerIndex - c.offset;
if (adjustment == c.length) {
// new slice would be empty, so remove instead
firstComponentId++;
} else {
Component newC = new Component(c.buf.slice(adjustment, c.length - adjustment));
components.set(firstComponentId, newC);
}
components.removeRange(0, firstComponentId);
// Update indexes and markers.
updateComponentOffsets(0);
setIndex(0, writerIndex - readerIndex);
adjustMarkers(readerIndex);
return this;
}
private ByteBuf allocBuffer(int capacity) {
return direct ? alloc().directBuffer(capacity) : alloc().heapBuffer(capacity);
}
@Override
public String toString() {
String result = super.toString();
result = result.substring(0, result.length() - 1);
return result + ", components=" + components.size() + ')';
}
private static final class Component {
final ByteBuf buf;
final int length;
int offset;
int endOffset;
Component(ByteBuf buf) {
this.buf = buf;
length = buf.readableBytes();
}
void freeIfNecessary() {
buf.release(); // We should not get a NPE here. If so, it must be a bug.
}
}
@Override
public CompositeByteBuf readerIndex(int readerIndex) {
return (CompositeByteBuf) super.readerIndex(readerIndex);
}
@Override
public CompositeByteBuf writerIndex(int writerIndex) {
return (CompositeByteBuf) super.writerIndex(writerIndex);
}
@Override
public CompositeByteBuf setIndex(int readerIndex, int writerIndex) {
return (CompositeByteBuf) super.setIndex(readerIndex, writerIndex);
}
@Override
public CompositeByteBuf clear() {
return (CompositeByteBuf) super.clear();
}
@Override
public CompositeByteBuf markReaderIndex() {
return (CompositeByteBuf) super.markReaderIndex();
}
@Override
public CompositeByteBuf resetReaderIndex() {
return (CompositeByteBuf) super.resetReaderIndex();
}
@Override
public CompositeByteBuf markWriterIndex() {
return (CompositeByteBuf) super.markWriterIndex();
}
@Override
public CompositeByteBuf resetWriterIndex() {
return (CompositeByteBuf) super.resetWriterIndex();
}
@Override
public CompositeByteBuf ensureWritable(int minWritableBytes) {
return (CompositeByteBuf) super.ensureWritable(minWritableBytes);
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuf dst) {
return (CompositeByteBuf) super.getBytes(index, dst);
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuf dst, int length) {
return (CompositeByteBuf) super.getBytes(index, dst, length);
}
@Override
public CompositeByteBuf getBytes(int index, byte[] dst) {
return (CompositeByteBuf) super.getBytes(index, dst);
}
@Override
public CompositeByteBuf setBoolean(int index, boolean value) {
return (CompositeByteBuf) super.setBoolean(index, value);
}
@Override
public CompositeByteBuf setChar(int index, int value) {
return (CompositeByteBuf) super.setChar(index, value);
}
@Override
public CompositeByteBuf setFloat(int index, float value) {
return (CompositeByteBuf) super.setFloat(index, value);
}
@Override
public CompositeByteBuf setDouble(int index, double value) {
return (CompositeByteBuf) super.setDouble(index, value);
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuf src) {
return (CompositeByteBuf) super.setBytes(index, src);
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuf src, int length) {
return (CompositeByteBuf) super.setBytes(index, src, length);
}
@Override
public CompositeByteBuf setBytes(int index, byte[] src) {
return (CompositeByteBuf) super.setBytes(index, src);
}
@Override
public CompositeByteBuf setZero(int index, int length) {
return (CompositeByteBuf) super.setZero(index, length);
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst) {
return (CompositeByteBuf) super.readBytes(dst);
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst, int length) {
return (CompositeByteBuf) super.readBytes(dst, length);
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst, int dstIndex, int length) {
return (CompositeByteBuf) super.readBytes(dst, dstIndex, length);
}
@Override
public CompositeByteBuf readBytes(byte[] dst) {
return (CompositeByteBuf) super.readBytes(dst);
}
@Override
public CompositeByteBuf readBytes(byte[] dst, int dstIndex, int length) {
return (CompositeByteBuf) super.readBytes(dst, dstIndex, length);
}
@Override
public CompositeByteBuf readBytes(ByteBuffer dst) {
return (CompositeByteBuf) super.readBytes(dst);
}
@Override
public CompositeByteBuf readBytes(OutputStream out, int length) throws IOException {
return (CompositeByteBuf) super.readBytes(out, length);
}
@Override
public CompositeByteBuf skipBytes(int length) {
return (CompositeByteBuf) super.skipBytes(length);
}
@Override
public CompositeByteBuf writeBoolean(boolean value) {
return (CompositeByteBuf) super.writeBoolean(value);
}
@Override
public CompositeByteBuf writeByte(int value) {
return (CompositeByteBuf) super.writeByte(value);
}
@Override
public CompositeByteBuf writeShort(int value) {
return (CompositeByteBuf) super.writeShort(value);
}
@Override
public CompositeByteBuf writeMedium(int value) {
return (CompositeByteBuf) super.writeMedium(value);
}
@Override
public CompositeByteBuf writeInt(int value) {
return (CompositeByteBuf) super.writeInt(value);
}
@Override
public CompositeByteBuf writeLong(long value) {
return (CompositeByteBuf) super.writeLong(value);
}
@Override
public CompositeByteBuf writeChar(int value) {
return (CompositeByteBuf) super.writeChar(value);
}
@Override
public CompositeByteBuf writeFloat(float value) {
return (CompositeByteBuf) super.writeFloat(value);
}
@Override
public CompositeByteBuf writeDouble(double value) {
return (CompositeByteBuf) super.writeDouble(value);
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src) {
return (CompositeByteBuf) super.writeBytes(src);
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src, int length) {
return (CompositeByteBuf) super.writeBytes(src, length);
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
return (CompositeByteBuf) super.writeBytes(src, srcIndex, length);
}
@Override
public CompositeByteBuf writeBytes(byte[] src) {
return (CompositeByteBuf) super.writeBytes(src);
}
@Override
public CompositeByteBuf writeBytes(byte[] src, int srcIndex, int length) {
return (CompositeByteBuf) super.writeBytes(src, srcIndex, length);
}
@Override
public CompositeByteBuf writeBytes(ByteBuffer src) {
return (CompositeByteBuf) super.writeBytes(src);
}
@Override
public CompositeByteBuf writeZero(int length) {
return (CompositeByteBuf) super.writeZero(length);
}
@Override
public CompositeByteBuf retain(int increment) {
return (CompositeByteBuf) super.retain(increment);
}
@Override
public CompositeByteBuf retain() {
return (CompositeByteBuf) super.retain();
}
@Override
public ByteBuffer[] nioBuffers() {
return nioBuffers(readerIndex(), readableBytes());
}
@Override
public CompositeByteBuf discardSomeReadBytes() {
return discardReadComponents();
}
@Override
protected void deallocate() {
if (freed) {
return;
}
freed = true;
int size = components.size();
// We're not using foreach to avoid creating an iterator.
// see https://github.com/netty/netty/issues/2642
for (int i = 0; i < size; i++) {
components.get(i).freeIfNecessary();
}
}
@Override
public ByteBuf unwrap() {
return null;
}
private final class CompositeByteBufIterator implements Iterator {
private final int size = components.size();
private int index;
@Override
public boolean hasNext() {
return size > index;
}
@Override
public ByteBuf next() {
if (size != components.size()) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return components.get(index++).buf;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read-Only");
}
}
private static final class ComponentList extends ArrayList {
ComponentList(int initialCapacity) {
super(initialCapacity);
}
// Expose this methods so we not need to create a new subList just to remove a range of elements.
@Override
public void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex, toIndex);
}
}
}