package org.deftserver.io.buffer; import java.nio.ByteBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; public class DynamicByteBuffer { private final static Logger logger = LoggerFactory.getLogger(DynamicByteBuffer.class); private ByteBuffer backend; private DynamicByteBuffer(ByteBuffer bb) { this.backend = bb; } /** * Allocate a new {@code DynamicByteBuffer} that will be using a {@ByteBuffer} internally. * @param capacity initial capacity */ public static DynamicByteBuffer allocate(int capacity) { return new DynamicByteBuffer(ByteBuffer.allocate(capacity)); } /** * Append the data. Will reallocate if needed. */ public void put(byte[] src) { ensureCapacity(src.length); backend.put(src); } /** * Prepend the data. Will reallocate if needed. */ public void prepend(String data) { byte[] bytes = data.getBytes(Charsets.UTF_8); int newSize = bytes.length + backend.position(); byte[] newBuffer = new byte[newSize]; System.arraycopy(bytes, 0, newBuffer, 0, bytes.length); // initial line and headers System.arraycopy(backend.array(), 0, newBuffer, bytes.length, backend.position()); // body backend = ByteBuffer.wrap(newBuffer); backend.position(newSize); } /** * Ensures that its safe to append size data to backend. * @param size The size of the data that is about to be appended. */ private void ensureCapacity(int size) { int remaining = backend.remaining(); if (size > remaining) { logger.debug("allocating new DynamicByteBuffer, old capacity {}: ", backend.capacity()); int missing = size - remaining; int newSize = (int) ((backend.capacity() + missing) * 1.5); reallocate(newSize); } } // Preserves position. private void reallocate(int newCapacity) { int oldPosition = backend.position(); byte[] newBuffer = new byte[newCapacity]; System.arraycopy(backend.array(), 0, newBuffer, 0, backend.position()); backend = ByteBuffer.wrap(newBuffer); backend.position(oldPosition); logger.debug("allocated new DynamicByteBufer, new capacity: {}", backend.capacity()); } /** * Returns the {@code ByteBuffer} that is used internally by this {@DynamicByteBufer}. * Changes made to the returned {@code ByteBuffer} will be incur modifications in this {@DynamicByteBufer}. */ public ByteBuffer getByteBuffer() { return backend; } /** * See {@link ByteBuffer#flip} */ public void flip() { backend.flip(); } /** * See {@link ByteBuffer#limit} */ public int limit() { return backend.limit(); } /** * See {@link ByteBuffer#position} */ public int position() { return backend.position(); } /** * See {@link ByteBuffer#array} */ public byte[] array() { return backend.array(); } /** * See {@link ByteBuffer#capacity} */ public int capacity() { return backend.capacity(); } /** * See {@link ByteBuffer#hasRemaining} */ public boolean hasRemaining() { return backend.hasRemaining(); } /** * See {@link ByteBuffer#compact} */ public DynamicByteBuffer compact() { backend.compact(); return this; } /** * See {@link ByteBuffer#clear} */ public DynamicByteBuffer clear() { backend.clear(); return this; } }