package voldemort.common.nio; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.lang.mutable.MutableLong; import voldemort.utils.ByteUtils; /* * ByteBufferContainer holds a reference to the ByteBuffer. This lets your share * the byte buffer with the input stream and the output stream when only one of * them can be in progress. For Voldemort this is always true, but most of the * admin code, does not clear the buffer once the contents are read in stream * processing. Hence the client and server can use the same buffer for read and * write to begin with. */ public class ByteBufferContainer { private ByteBuffer buffer; private final int sizeLowerBound; private final int sizeUpperBound; /** * Reference to a size tracking object, that tracks the size of the buffer * in bytes */ private final MutableLong sizeTracker; private final AtomicBoolean isClosed; private void assignBuffer(ByteBuffer newBuffer) { if(sizeTracker != null) { int oldSize = 0; if(this.buffer != null ){ oldSize = this.buffer.capacity(); } updateSizeStats(oldSize, newBuffer.capacity()); } this.buffer = newBuffer; } public ByteBufferContainer(int sizeLowerBound, int sizeUpperBound, MutableLong sizeTracker) { this.sizeTracker = sizeTracker; this.sizeLowerBound = sizeLowerBound; this.sizeUpperBound = sizeUpperBound; isClosed = new AtomicBoolean(false); assignBuffer(ByteBuffer.allocate(sizeLowerBound)); } public ByteBufferContainer(ByteBuffer byteBuffer) { if(byteBuffer == null) { throw new IllegalArgumentException("byteBuffer is null"); } this.buffer = byteBuffer; this.sizeLowerBound = byteBuffer.capacity(); this.sizeUpperBound = byteBuffer.capacity() * 2; this.sizeTracker = null; isClosed = new AtomicBoolean(false); } public void growBuffer() { growBuffer(buffer.capacity() * 2); } public void growBuffer(int newSize) { int oldSize = buffer.capacity(); if(newSize > oldSize) { assignBuffer(ByteUtils.expand(this.buffer, newSize)); } } public void ensureSpace(int writeLen) { int need = (writeLen - this.buffer.remaining()); if(need <= 0) { return; } growBuffer(buffer.capacity() + need); } public ByteBuffer getBuffer() { return this.buffer; } public void reset() { if(this.buffer.capacity() > sizeUpperBound) { assignBuffer(ByteBuffer.allocate(sizeLowerBound)); } this.buffer.clear(); } public void close() { if(isClosed.compareAndSet(false, true)) { updateSizeStats(buffer.capacity(), 0); } } private void updateSizeStats(int oldSize, int newSize) { if(sizeTracker != null) { sizeTracker.subtract(oldSize); sizeTracker.add(newSize); } } }