Author: trustin
Date: Sat Nov 10 09:21:20 2007
New Revision: 593795
URL: http://svn.apache.org/viewvc?rev=593795&view=rev
Log:
Added autoShrink feature to IoBuffer.
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/AbstractIoBuffer.java
mina/trunk/core/src/main/java/org/apache/mina/common/CachedBufferAllocator.java
mina/trunk/core/src/main/java/org/apache/mina/common/IoBuffer.java
mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferAllocator.java
mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferWrapper.java
mina/trunk/core/src/main/java/org/apache/mina/common/SimpleBufferAllocator.java
mina/trunk/core/src/test/java/org/apache/mina/common/IoBufferTest.java
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/AbstractIoBuffer.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/AbstractIoBuffer.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/common/AbstractIoBuffer.java
(original)
+++ mina/trunk/core/src/main/java/org/apache/mina/common/AbstractIoBuffer.java
Sat Nov 10 09:21:20 2007
@@ -37,13 +37,16 @@
*
* @author The Apache MINA Project ([EMAIL PROTECTED])
* @version $Rev$, $Date$
- * @noinspection StaticNonFinalField
* @see IoBufferAllocator
*/
public abstract class AbstractIoBuffer extends IoBuffer {
+
+ private final IoBufferAllocator allocator;
+ private final int initialCapacity;
private final boolean derived;
private boolean autoExpand;
- private boolean autoExpandAllowed = true;
+ private boolean autoShrink;
+ private boolean recapacityAllowed = true;
/**
* We don't have any access to Buffer.markValue(), so we need to track it
down,
@@ -51,9 +54,25 @@
*/
private int mark = -1;
- protected AbstractIoBuffer(boolean autoExpandAllowed) {
- this.autoExpandAllowed = autoExpandAllowed;
- this.derived = !autoExpandAllowed;
+ /**
+ * Creates a new parent buffer.
+ */
+ protected AbstractIoBuffer(
+ IoBufferAllocator allocator, int initialCapacity) {
+ this.allocator = allocator;
+ this.recapacityAllowed = true;
+ this.derived = false;
+ this.initialCapacity = initialCapacity;
+ }
+
+ /**
+ * Creates a new derived buffer.
+ */
+ protected AbstractIoBuffer(AbstractIoBuffer parent) {
+ this.allocator = parent.allocator;
+ this.recapacityAllowed = false;
+ this.derived = true;
+ this.initialCapacity = parent.initialCapacity;
}
@Override
@@ -66,6 +85,11 @@
return buf().isReadOnly();
}
+ /**
+ * Sets the underlying NIO buffer instance.
+ */
+ protected abstract void buf(ByteBuffer newBuf);
+
@Override
public int capacity() {
return buf().capacity();
@@ -73,13 +97,28 @@
@Override
public IoBuffer capacity(int newCapacity) {
+ if (!recapacityAllowed) {
+ throw new IllegalStateException(
+ "Derived buffers and their parent can't be expanded.");
+ }
+
+ // Allocate a new buffer and transfer all settings to it.
if (newCapacity > capacity()) {
- // Allocate a new buffer and transfer all settings to it.
+ // Expand:
+ //// Save the state.
int pos = position();
int limit = limit();
ByteOrder bo = order();
- capacity0(newCapacity);
+ //// Reallocate.
+ ByteBuffer oldBuf = buf();
+ ByteBuffer newBuf =
+ allocator.allocateNioBuffer(newCapacity, isDirect());
+ oldBuf.clear();
+ newBuf.put(oldBuf);
+ buf(newBuf);
+
+ //// Restore the state.
buf().limit(limit);
if (mark >= 0) {
buf().position(mark);
@@ -88,19 +127,18 @@
buf().position(pos);
buf().order(bo);
}
-
+
return this;
}
- /**
- * Implement this method to increase the capacity of this buffer.
- * <tt>newCapacity</tt> is always greater than the current capacity.
- */
- protected abstract void capacity0(int newCapacity);
-
@Override
public boolean isAutoExpand() {
- return autoExpand && autoExpandAllowed;
+ return autoExpand && recapacityAllowed;
+ }
+
+ @Override
+ public boolean isAutoShrink() {
+ return autoShrink && recapacityAllowed;
}
@Override
@@ -110,16 +148,31 @@
@Override
public IoBuffer setAutoExpand(boolean autoExpand) {
- if (!autoExpandAllowed) {
+ if (!recapacityAllowed) {
throw new IllegalStateException(
- "Derived buffers can't be auto-expandable.");
+ "Derived buffers and their parent can't be expanded.");
}
this.autoExpand = autoExpand;
return this;
}
@Override
+ public IoBuffer setAutoShrink(boolean autoShrink) {
+ if (!recapacityAllowed) {
+ throw new IllegalStateException(
+ "Derived buffers and their parent can't be shrinked.");
+ }
+ this.autoShrink = autoShrink;
+ return this;
+ }
+
+ @Override
public IoBuffer expand(int pos, int expectedRemaining) {
+ if (!recapacityAllowed) {
+ throw new IllegalStateException(
+ "Derived buffers and their parent can't be expanded.");
+ }
+
int end = pos + expectedRemaining;
if (end > capacity()) {
// The buffer needs expansion.
@@ -248,7 +301,38 @@
@Override
public IoBuffer compact() {
- buf().compact();
+ int remaining = remaining();
+ if (isAutoShrink() && remaining <= capacity() >>> 2) {
+ int newCapacity = remaining << 1;
+ if (newCapacity < initialCapacity && initialCapacity ==
capacity()) {
+ buf().compact();
+ } else {
+ newCapacity = Math.max(initialCapacity, newCapacity);
+
+ // Shrink and compact:
+ //// Save the state.
+ ByteOrder bo = order();
+
+ //// Sanity check.
+ if (remaining > newCapacity) {
+ throw new IllegalStateException(
+ "The amount of the remaining bytes is greater than
" +
+ "the new capacity.");
+ }
+
+ //// Reallocate.
+ ByteBuffer oldBuf = buf();
+ ByteBuffer newBuf =
+ allocator.allocateNioBuffer(newCapacity, isDirect());
+ newBuf.put(oldBuf);
+ buf(newBuf);
+
+ //// Restore the state.
+ buf().order(bo);
+ }
+ } else {
+ buf().compact();
+ }
mark = -1;
return this;
}
@@ -440,7 +524,7 @@
@Override
public final IoBuffer asReadOnlyBuffer() {
- autoExpandAllowed = false;
+ recapacityAllowed = false;
return asReadOnlyBuffer0();
}
@@ -452,7 +536,7 @@
@Override
public final IoBuffer duplicate() {
- autoExpandAllowed = false;
+ recapacityAllowed = false;
return duplicate0();
}
@@ -464,7 +548,7 @@
@Override
public final IoBuffer slice() {
- autoExpandAllowed = false;
+ recapacityAllowed = false;
return slice0();
}
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/CachedBufferAllocator.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/CachedBufferAllocator.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
---
mina/trunk/core/src/main/java/org/apache/mina/common/CachedBufferAllocator.java
(original)
+++
mina/trunk/core/src/main/java/org/apache/mina/common/CachedBufferAllocator.java
Sat Nov 10 09:21:20 2007
@@ -177,8 +177,12 @@
return buf;
}
+ public ByteBuffer allocateNioBuffer(int capacity, boolean direct) {
+ return allocate(capacity, direct).buf();
+ }
+
public IoBuffer wrap(ByteBuffer nioBuffer) {
- return new CachedBuffer(nioBuffer, true);
+ return new CachedBuffer(nioBuffer);
}
public void dispose() {
@@ -211,12 +215,18 @@
private final Thread ownerThread;
private ByteBuffer buf;
- protected CachedBuffer(ByteBuffer buf, boolean autoExpandAllowed) {
- super(autoExpandAllowed);
+ protected CachedBuffer(ByteBuffer buf) {
+ super(CachedBufferAllocator.this, buf.capacity());
this.ownerThread = Thread.currentThread();
this.buf = buf;
buf.order(ByteOrder.BIG_ENDIAN);
}
+
+ protected CachedBuffer(CachedBuffer parent, ByteBuffer buf) {
+ super(parent);
+ this.ownerThread = Thread.currentThread();
+ this.buf = buf;
+ }
@Override
public ByteBuffer buf() {
@@ -225,33 +235,27 @@
}
return buf;
}
-
+
@Override
- protected void capacity0(int requestedCapacity) {
- int newCapacity = normalizeCapacity(requestedCapacity);
-
- ByteBuffer oldBuf = buf();
- ByteBuffer newBuf = allocate(newCapacity, isDirect()).buf();
- oldBuf.clear();
- newBuf.put(oldBuf);
- this.buf = newBuf;
-
+ protected void buf(ByteBuffer buf) {
+ ByteBuffer oldBuf = this.buf;
+ this.buf = buf;
free(oldBuf);
}
@Override
protected IoBuffer duplicate0() {
- return new CachedBuffer(buf().duplicate(), false);
+ return new CachedBuffer(this, buf().duplicate());
}
@Override
protected IoBuffer slice0() {
- return new CachedBuffer(buf().slice(), false);
+ return new CachedBuffer(this, buf().slice());
}
@Override
protected IoBuffer asReadOnlyBuffer0() {
- return new CachedBuffer(buf().asReadOnlyBuffer(), false);
+ return new CachedBuffer(this, buf().asReadOnlyBuffer());
}
@Override
@@ -296,7 +300,7 @@
// Restrict the size of the pool to prevent OOM.
if (maxPoolSize == 0 || pool.size() < maxPoolSize) {
- pool.offer(new CachedBuffer(oldBuf, true));
+ pool.offer(new CachedBuffer(oldBuf));
}
}
}
Modified: mina/trunk/core/src/main/java/org/apache/mina/common/IoBuffer.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/IoBuffer.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/common/IoBuffer.java
(original)
+++ mina/trunk/core/src/main/java/org/apache/mina/common/IoBuffer.java Sat Nov
10 09:21:20 2007
@@ -103,6 +103,20 @@
* increase by two times, and its limit will increase to the last position
* the string is written.
* </p>
+ *
+ * <h2>AutoShrink</h2>
+ * <p>
+ * You might also want to decrease the capacity of the buffer when most
+ * of the allocated memory area is not being used. MINA [EMAIL PROTECTED]
IoBuffer}
+ * provides <tt>autoShrink</tt> property to take care of this issue.
+ * If <tt>autoShrink</tt> is turned on, [EMAIL PROTECTED] IoBuffer} halves the
capacity
+ * of the buffer when [EMAIL PROTECTED] #compact()} is invoked and only 1/4 or
less of
+ * the current capacity is being used. NIO <tt>ByteBuffer</tt> is reallocated
+ * by MINA [EMAIL PROTECTED] IoBuffer} behind the scene, and therefore [EMAIL
PROTECTED] #buf()}
+ * will return a different [EMAIL PROTECTED] ByteBuffer} instance once
capacity is
+ * changed. Please also note [EMAIL PROTECTED] #compact()} will not decrease
the
+ * capacity if the new capacity is less than the initial capacity of the
+ * buffer.
*
* <h2>Derived Buffers</h2>
* <p>
@@ -110,9 +124,9 @@
* [EMAIL PROTECTED] #duplicate()}, [EMAIL PROTECTED] #slice()}, or [EMAIL
PROTECTED] #asReadOnlyBuffer()}.
* They are useful especially when you broadcast the same messages to
* multiple [EMAIL PROTECTED] IoSession}s. Please note that the buffer
derived from and
- * its derived buffers are not both auto-expandable. Trying to call
- * [EMAIL PROTECTED] #setAutoExpand(boolean)} with <tt>true</tt> parameter will
- * raise an [EMAIL PROTECTED] IllegalStateException}.
+ * its derived buffers are not both auto-expandable neither auto-shrinkable.
+ * Trying to call [EMAIL PROTECTED] #setAutoExpand(boolean)} or [EMAIL
PROTECTED] #setAutoShrink(boolean)}
+ * with <tt>true</tt> parameter will raise an [EMAIL PROTECTED]
IllegalStateException}.
* </p>
*
* <h2>Changing Buffer Allocation Policy</h2>
@@ -129,7 +143,7 @@
*
* @author The Apache MINA Project ([EMAIL PROTECTED])
* @version $Rev$, $Date$
- * @noinspection StaticNonFinalField
+ *
* @see IoBufferAllocator
*/
public abstract class IoBuffer implements Comparable<IoBuffer> {
@@ -247,7 +261,7 @@
* Returns the underlying NIO buffer instance.
*/
public abstract ByteBuffer buf();
-
+
/**
* @see ByteBuffer#isDirect()
*/
@@ -270,7 +284,11 @@
public abstract int capacity();
/**
- * Changes the capacity of this buffer.
+ * Increases the capacity of this buffer. If the new capacity is less than
+ * or equal to the current capacity, this method returns silently. If the
+ * new capacity is greater than the current capacity, the buffer is
+ * reallocated while retaining the position, limit, mark and the content
+ * of the buffer.
*/
public abstract IoBuffer capacity(int newCapacity);
@@ -283,6 +301,16 @@
* Turns on or off <tt>autoExpand</tt>.
*/
public abstract IoBuffer setAutoExpand(boolean autoExpand);
+
+ /**
+ * Returns <tt>true</tt> if and only if <tt>autoShrink</tt> is turned on.
+ */
+ public abstract boolean isAutoShrink();
+
+ /**
+ * Turns on or off <tt>autoShrink</tt>.
+ */
+ public abstract IoBuffer setAutoShrink(boolean autoShrink);
/**
* Changes the capacity and limit of this buffer so this buffer get
@@ -297,11 +325,11 @@
/**
* Changes the capacity and limit of this buffer so this buffer get
* the specified <tt>expectedRemaining</tt> room from the specified
- * <tt>pos</tt>.
+ * <tt>position</tt>.
* This method works even if you didn't set <tt>autoExpand</tt> to
* <tt>true</tt>.
*/
- public abstract IoBuffer expand(int pos, int expectedRemaining);
+ public abstract IoBuffer expand(int position, int expectedRemaining);
/**
* @see java.nio.Buffer#position()
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferAllocator.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferAllocator.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferAllocator.java
(original)
+++ mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferAllocator.java
Sat Nov 10 09:21:20 2007
@@ -39,6 +39,15 @@
IoBuffer allocate(int capacity, boolean direct);
/**
+ * Returns the NIO buffer which is capable of the specified size.
+ *
+ * @param capacity the capacity of the buffer
+ * @param direct <tt>true</tt> to get a direct buffer,
+ * <tt>false</tt> to get a heap buffer.
+ */
+ ByteBuffer allocateNioBuffer(int capacity, boolean direct);
+
+ /**
* Wraps the specified NIO [EMAIL PROTECTED] ByteBuffer} into MINA buffer.
*/
IoBuffer wrap(ByteBuffer nioBuffer);
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferWrapper.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferWrapper.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferWrapper.java
(original)
+++ mina/trunk/core/src/main/java/org/apache/mina/common/IoBufferWrapper.java
Sat Nov 10 09:21:20 2007
@@ -646,4 +646,15 @@
public boolean isDerived() {
return buf.isDerived();
}
+
+ @Override
+ public boolean isAutoShrink() {
+ return buf.isAutoShrink();
+ }
+
+ @Override
+ public IoBuffer setAutoShrink(boolean autoShrink) {
+ buf.setAutoShrink(autoShrink);
+ return this;
+ }
}
Modified:
mina/trunk/core/src/main/java/org/apache/mina/common/SimpleBufferAllocator.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/common/SimpleBufferAllocator.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
---
mina/trunk/core/src/main/java/org/apache/mina/common/SimpleBufferAllocator.java
(original)
+++
mina/trunk/core/src/main/java/org/apache/mina/common/SimpleBufferAllocator.java
Sat Nov 10 09:21:20 2007
@@ -33,90 +33,63 @@
public class SimpleBufferAllocator implements IoBufferAllocator {
public IoBuffer allocate(int capacity, boolean direct) {
+ return wrap(allocateNioBuffer(capacity, direct));
+ }
+
+ public ByteBuffer allocateNioBuffer(int capacity, boolean direct) {
ByteBuffer nioBuffer;
if (direct) {
nioBuffer = ByteBuffer.allocateDirect(capacity);
} else {
nioBuffer = ByteBuffer.allocate(capacity);
}
- return new SimpleBuffer(nioBuffer, true);
+ return nioBuffer;
}
public IoBuffer wrap(ByteBuffer nioBuffer) {
- return new SimpleBuffer(nioBuffer, true);
+ return new SimpleBuffer(nioBuffer);
}
public void dispose() {
}
- private static class SimpleBuffer extends AbstractIoBuffer {
+ private class SimpleBuffer extends AbstractIoBuffer {
private ByteBuffer buf;
- protected SimpleBuffer(ByteBuffer buf,
- boolean autoExpandAllowed) {
- super(autoExpandAllowed);
+ protected SimpleBuffer(ByteBuffer buf) {
+ super(SimpleBufferAllocator.this, buf.capacity());
this.buf = buf;
buf.order(ByteOrder.BIG_ENDIAN);
}
- @Override
- public ByteBuffer buf() {
- return buf;
+ protected SimpleBuffer(SimpleBuffer parent, ByteBuffer buf) {
+ super(parent);
+ this.buf = buf;
}
@Override
- protected void capacity0(int requestedCapacity) {
- int newCapacity = normalizeCapacity(requestedCapacity);
-
- ByteBuffer oldBuf = this.buf;
- ByteBuffer newBuf;
- if (isDirect()) {
- newBuf = ByteBuffer.allocateDirect(newCapacity);
- } else {
- newBuf = ByteBuffer.allocate(newCapacity);
- }
-
- newBuf.clear();
- oldBuf.clear();
- newBuf.put(oldBuf);
- this.buf = newBuf;
+ public ByteBuffer buf() {
+ return buf;
}
- private static int normalizeCapacity(int requestedCapacity) {
- switch (requestedCapacity) {
- case 1 << 0: case 1 << 1: case 1 << 2: case 1 << 3: case 1 <<
4:
- case 1 << 5: case 1 << 6: case 1 << 7: case 1 << 8: case 1 <<
9:
- case 1 << 10: case 1 << 11: case 1 << 12: case 1 << 13: case 1 <<
14:
- case 1 << 15: case 1 << 16: case 1 << 17: case 1 << 18: case 1 <<
19:
- case 1 << 21: case 1 << 22: case 1 << 23: case 1 << 24: case 1 <<
25:
- case 1 << 26: case 1 << 27: case 1 << 28: case 1 << 29: case 1 <<
30:
- case Integer.MAX_VALUE:
- return requestedCapacity;
- }
-
- int newCapacity = 1;
- while (newCapacity < requestedCapacity) {
- newCapacity <<= 1;
- if (newCapacity < 0) {
- return Integer.MAX_VALUE;
- }
- }
- return newCapacity;
+ @Override
+ protected void buf(ByteBuffer buf) {
+ this.buf = buf;
}
@Override
protected IoBuffer duplicate0() {
- return new SimpleBuffer(this.buf.duplicate(), false);
+ return new SimpleBuffer(this, this.buf.duplicate());
}
@Override
protected IoBuffer slice0() {
- return new SimpleBuffer(this.buf.slice(), false);
+ return new SimpleBuffer(this, this.buf.slice());
}
@Override
protected IoBuffer asReadOnlyBuffer0() {
- return new SimpleBuffer(this.buf.asReadOnlyBuffer(), false);
+ return new SimpleBuffer(this, this.buf.asReadOnlyBuffer());
}
@Override
Modified: mina/trunk/core/src/test/java/org/apache/mina/common/IoBufferTest.java
URL:
http://svn.apache.org/viewvc/mina/trunk/core/src/test/java/org/apache/mina/common/IoBufferTest.java?rev=593795&r1=593794&r2=593795&view=diff
==============================================================================
--- mina/trunk/core/src/test/java/org/apache/mina/common/IoBufferTest.java
(original)
+++ mina/trunk/core/src/test/java/org/apache/mina/common/IoBufferTest.java Sat
Nov 10 09:21:20 2007
@@ -116,6 +116,80 @@
buf.reset();
Assert.assertEquals(3, buf.position());
}
+
+ public void testAutoShrink() throws Exception {
+ IoBuffer buf = IoBuffer.allocate(8).setAutoShrink(true);
+
+ // Make sure the buffer doesn't shrink too much (less than the initial
+ // capacity.)
+ buf.sweep((byte) 1);
+ buf.fill(7);
+ buf.compact();
+ Assert.assertEquals(8, buf.capacity());
+ Assert.assertEquals(1, buf.position());
+ Assert.assertEquals(8, buf.limit());
+ buf.clear();
+ Assert.assertEquals(1, buf.get());
+
+ // Expand the buffer.
+ buf.capacity(32).clear();
+ Assert.assertEquals(32, buf.capacity());
+
+ // Make sure the buffer shrinks when only 1/4 is being used.
+ buf.sweep((byte) 1);
+ buf.fill(24);
+ buf.compact();
+ Assert.assertEquals(16, buf.capacity());
+ Assert.assertEquals(8, buf.position());
+ Assert.assertEquals(16, buf.limit());
+ buf.clear();
+ for (int i = 0; i < 8; i ++) {
+ Assert.assertEquals(1, buf.get());
+ }
+
+ // Expand the buffer.
+ buf.capacity(32).clear();
+ Assert.assertEquals(32, buf.capacity());
+
+ // Make sure the buffer shrinks when only 1/8 is being used.
+ buf.sweep((byte) 1);
+ buf.fill(28);
+ buf.compact();
+ Assert.assertEquals(8, buf.capacity());
+ Assert.assertEquals(4, buf.position());
+ Assert.assertEquals(8, buf.limit());
+ buf.clear();
+ for (int i = 0; i < 4; i ++) {
+ Assert.assertEquals(1, buf.get());
+ }
+
+ // Expand the buffer.
+ buf.capacity(32).clear();
+ Assert.assertEquals(32, buf.capacity());
+
+ // Make sure the buffer shrinks when 0 byte is being used.
+ buf.fill(32);
+ buf.compact();
+ Assert.assertEquals(8, buf.capacity());
+ Assert.assertEquals(0, buf.position());
+ Assert.assertEquals(8, buf.limit());
+
+ // Expand the buffer.
+ buf.capacity(32).clear();
+ Assert.assertEquals(32, buf.capacity());
+
+ // Make sure the buffer doesn't shrink when more than 1/4 is being
used.
+ buf.sweep((byte) 1);
+ buf.fill(23);
+ buf.compact();
+ Assert.assertEquals(32, buf.capacity());
+ Assert.assertEquals(9, buf.position());
+ Assert.assertEquals(32, buf.limit());
+ buf.clear();
+ for (int i = 0; i < 9; i ++) {
+ Assert.assertEquals(1, buf.get());
+ }
+ }
public void testGetString() throws Exception {
IoBuffer buf = IoBuffer.allocate(16);