org.h2.mvstore.WriteBuffer Maven / Gradle / Ivy
/*
* Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore;
import java.nio.ByteBuffer;
/**
* An auto-resize buffer to write data into a ByteBuffer.
*/
public class WriteBuffer {
/**
* The maximum size of the buffer in order to be re-used after a clear
* operation.
*/
private static final int MAX_REUSE_CAPACITY = 4 * 1024 * 1024;
/**
* The minimum number of bytes to grow a buffer at a time.
*/
private static final int MIN_GROW = 1024 * 1024;
/**
* The buffer that is used after a clear operation.
*/
private ByteBuffer reuse;
/**
* The current buffer (may be replaced if it is too small).
*/
private ByteBuffer buff;
public WriteBuffer(int initialSize) {
reuse = ByteBuffer.allocate(initialSize);
buff = reuse;
}
public WriteBuffer() {
this(MIN_GROW);
}
/**
* Write a variable size integer.
*
* @param x the value
* @return this
*/
public WriteBuffer putVarInt(int x) {
DataUtils.writeVarInt(ensureCapacity(5), x);
return this;
}
/**
* Write a variable size long.
*
* @param x the value
* @return this
*/
public WriteBuffer putVarLong(long x) {
DataUtils.writeVarLong(ensureCapacity(10), x);
return this;
}
/**
* Write the characters of a string in a format similar to UTF-8.
*
* @param s the string
* @param len the number of characters to write
* @return this
*/
public WriteBuffer putStringData(String s, int len) {
ByteBuffer b = ensureCapacity(3 * len);
DataUtils.writeStringData(b, s, len);
return this;
}
/**
* Put a byte.
*
* @param x the value
* @return this
*/
public WriteBuffer put(byte x) {
ensureCapacity(1).put(x);
return this;
}
/**
* Put a character.
*
* @param x the value
* @return this
*/
public WriteBuffer putChar(char x) {
ensureCapacity(2).putChar(x);
return this;
}
/**
* Put a short.
*
* @param x the value
* @return this
*/
public WriteBuffer putShort(short x) {
ensureCapacity(2).putShort(x);
return this;
}
/**
* Put an integer.
*
* @param x the value
* @return this
*/
public WriteBuffer putInt(int x) {
ensureCapacity(4).putInt(x);
return this;
}
/**
* Put a long.
*
* @param x the value
* @return this
*/
public WriteBuffer putLong(long x) {
ensureCapacity(8).putLong(x);
return this;
}
/**
* Put a float.
*
* @param x the value
* @return this
*/
public WriteBuffer putFloat(float x) {
ensureCapacity(4).putFloat(x);
return this;
}
/**
* Put a double.
*
* @param x the value
* @return this
*/
public WriteBuffer putDouble(double x) {
ensureCapacity(8).putDouble(x);
return this;
}
/**
* Put a byte array.
*
* @param bytes the value
* @return this
*/
public WriteBuffer put(byte[] bytes) {
ensureCapacity(bytes.length).put(bytes);
return this;
}
/**
* Put a byte array.
*
* @param bytes the value
* @param offset the source offset
* @param length the number of bytes
* @return this
*/
public WriteBuffer put(byte[] bytes, int offset, int length) {
ensureCapacity(length).put(bytes, offset, length);
return this;
}
/**
* Put the contents of a byte buffer.
*
* @param src the source buffer
* @return this
*/
public WriteBuffer put(ByteBuffer src) {
ensureCapacity(src.remaining()).put(src);
return this;
}
/**
* Set the limit, possibly growing the buffer.
*
* @param newLimit the new limit
* @return this
*/
public WriteBuffer limit(int newLimit) {
ensureCapacity(newLimit - buff.position()).limit(newLimit);
return this;
}
/**
* Get the capacity.
*
* @return the capacity
*/
public int capacity() {
return buff.capacity();
}
/**
* Set the position.
*
* @param newPosition the new position
* @return the new position
*/
public WriteBuffer position(int newPosition) {
buff.position(newPosition);
return this;
}
/**
* Get the limit.
*
* @return the limit
*/
public int limit() {
return buff.limit();
}
/**
* Get the current position.
*
* @return the position
*/
public int position() {
return buff.position();
}
/**
* Copy the data into the destination array.
*
* @param dst the destination array
* @return this
*/
public WriteBuffer get(byte[] dst) {
buff.get(dst);
return this;
}
/**
* Update an integer at the given index.
*
* @param index the index
* @param value the value
* @return this
*/
public WriteBuffer putInt(int index, int value) {
buff.putInt(index, value);
return this;
}
/**
* Update a short at the given index.
*
* @param index the index
* @param value the value
* @return this
*/
public WriteBuffer putShort(int index, short value) {
buff.putShort(index, value);
return this;
}
/**
* Clear the buffer after use.
*
* @return this
*/
public WriteBuffer clear() {
if (buff.limit() > MAX_REUSE_CAPACITY) {
buff = reuse;
} else if (buff != reuse) {
reuse = buff;
}
buff.clear();
return this;
}
/**
* Get the byte buffer.
*
* @return the byte buffer
*/
public ByteBuffer getBuffer() {
return buff;
}
private ByteBuffer ensureCapacity(int len) {
if (buff.remaining() < len) {
grow(len);
}
return buff;
}
private void grow(int additional) {
ByteBuffer temp = buff;
int needed = additional - temp.remaining();
// grow at least MIN_GROW
long grow = Math.max(needed, MIN_GROW);
// grow at least 50% of the current size
grow = Math.max(temp.capacity() / 2, grow);
// the new capacity is at most Integer.MAX_VALUE
int newCapacity = (int) Math.min(Integer.MAX_VALUE, temp.capacity() + grow);
if (newCapacity < needed) {
throw new OutOfMemoryError("Capacity: " + newCapacity + " needed: " + needed);
}
try {
buff = ByteBuffer.allocate(newCapacity);
} catch (OutOfMemoryError e) {
throw new OutOfMemoryError("Capacity: " + newCapacity);
}
temp.flip();
buff.put(temp);
if (newCapacity <= MAX_REUSE_CAPACITY) {
reuse = buff;
}
}
}