Buffer.java
// Copyright (c) ZeroC, Inc.
package com.zeroc.Ice;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* An instance of java.nio.ByteBuffer cannot grow beyond its initial capacity. This class wraps a
* ByteBuffer and supports reallocation.
*
* @hidden Public because it's used by IceBT, SSL, and the 'Ice/background' test.
*/
public class Buffer {
public Buffer(boolean direct) {
this(direct, ByteOrder.LITTLE_ENDIAN);
}
public Buffer(boolean direct, ByteOrder order) {
b = _emptyBuffer;
_size = 0;
_capacity = 0;
_direct = direct;
_order = order;
}
public Buffer(byte[] data) {
this(data, ByteOrder.LITTLE_ENDIAN);
}
public Buffer(byte[] data, ByteOrder order) {
b = ByteBuffer.wrap(data);
b.order(order);
_size = data.length;
_capacity = 0;
_direct = false;
_order = order;
}
public Buffer(ByteBuffer data) {
this(data, ByteOrder.LITTLE_ENDIAN);
}
public Buffer(ByteBuffer data, ByteOrder order) {
b = data;
b.order(order);
_size = data.remaining();
_capacity = 0;
_direct = false;
_order = order;
}
public Buffer(Buffer buf, boolean adopt) {
b = buf.b;
_size = buf._size;
_capacity = buf._capacity;
_direct = buf._direct;
_shrinkCounter = buf._shrinkCounter;
_order = buf._order;
if (adopt) {
buf.clear();
}
}
public java.nio.Buffer position(int newPosition) {
// Cast to java.nio.Buffer to avoid incompatible covariant
// return type used in Java 9 java.nio.ByteBuffer
return ((java.nio.Buffer) b).position(newPosition);
}
public java.nio.Buffer limit(int newLimit) {
// Cast to java.nio.Buffer to avoid incompatible covariant
// return type used in Java 9 java.nio.ByteBuffer
return ((java.nio.Buffer) b).limit(newLimit);
}
public java.nio.Buffer flip() {
// Cast to java.nio.Buffer to avoid incompatible covariant
// return type used in Java 9 java.nio.ByteBuffer
return ((java.nio.Buffer) b).flip();
}
public void swap(Buffer buf) {
final ByteBuffer bb = buf.b;
final int size = buf._size;
final int capacity = buf._capacity;
final boolean direct = buf._direct;
final int shrinkCounter = buf._shrinkCounter;
final ByteOrder order = buf._order;
buf.b = b;
buf._size = _size;
buf._capacity = _capacity;
buf._direct = _direct;
buf._shrinkCounter = _shrinkCounter;
buf._order = _order;
b = bb;
_size = size;
_capacity = capacity;
_direct = direct;
_shrinkCounter = shrinkCounter;
_order = order;
}
public int size() {
return _size;
}
public boolean empty() {
return _size == 0;
}
public void clear() {
b = _emptyBuffer;
_size = 0;
_capacity = 0;
_shrinkCounter = 0;
}
//
// Call expand(n) to add room for n additional bytes. Note that expand()
// examines the current position of the buffer first; we don't want to expand the buffer if the
// caller is writing to a location that is already in the buffer.
//
public void expand(int n) {
final int sz = b == _emptyBuffer ? n : b.position() + n;
if (sz > _size) {
resize(sz, false);
}
}
public void resize(int n, boolean reading) {
assert (b == _emptyBuffer || _capacity > 0);
if (n == 0) {
clear();
} else if (n > _capacity) {
reserve(n);
}
_size = n;
//
// When used for reading, we want to set the buffer's limit to the new size.
//
if (reading) {
limit(_size);
}
}
public void reset() {
if (_size > 0 && _size * 2 < _capacity) {
//
// If the current buffer size is smaller than the buffer capacity, we shrink the buffer
// memory to the current size. This is to avoid holding on to too much memory if it's
// not needed anymore.
//
if (++_shrinkCounter > 2) {
reserve(_size);
_shrinkCounter = 0;
}
} else {
_shrinkCounter = 0;
}
_size = 0;
if (b != _emptyBuffer) {
limit(b.capacity());
position(0);
}
}
private void reserve(int n) {
if (n > _capacity) {
_capacity = java.lang.Math.max(n, 2 * _capacity);
_capacity = java.lang.Math.max(240, _capacity);
} else if (n < _capacity) {
_capacity = n;
} else {
return;
}
try {
ByteBuffer buf;
if (_direct) {
buf = ByteBuffer.allocateDirect(_capacity);
} else {
buf = ByteBuffer.allocate(_capacity);
}
if (b == _emptyBuffer) {
b = buf;
} else {
final int pos = b.position();
position(0);
limit(java.lang.Math.min(_capacity, b.capacity()));
buf.put(b);
b = buf;
limit(b.capacity());
position(pos);
}
b.order(_order); // Preserve the original order.
} catch (OutOfMemoryError ex) {
_capacity = b.capacity(); // Restore the previous capacity.
throw ex;
}
}
public ByteBuffer b;
// Sentinel used for null buffer.
public ByteBuffer _emptyBuffer = ByteBuffer.allocate(0);
private int _size;
private int _capacity; // Cache capacity to avoid excessive method calls.
private boolean _direct; // Use direct buffers?
private int _shrinkCounter;
private ByteOrder _order;
}