You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tr...@apache.org on 2006/03/22 09:28:56 UTC

svn commit: r387793 - in /directory/trunks/mina/core/src: main/java/org/apache/mina/common/ test/java/org/apache/mina/common/

Author: trustin
Date: Wed Mar 22 00:28:53 2006
New Revision: 387793

URL: http://svn.apache.org/viewcvs?rev=387793&view=rev
Log:
Resolved issue: DIRMINA-165 (Easy and performant copy of the ByteBuffer)
* Added duplicate(), slice(), and asReadOnlyBuffer() to Bytebuffer
* Implemented these three methods for all allocator implementations

Modified:
    directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBuffer.java
    directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBufferProxy.java
    directory/trunks/mina/core/src/main/java/org/apache/mina/common/PooledByteBufferAllocator.java
    directory/trunks/mina/core/src/main/java/org/apache/mina/common/SimpleByteBufferAllocator.java
    directory/trunks/mina/core/src/test/java/org/apache/mina/common/ByteBufferTest.java

Modified: directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBuffer.java
URL: http://svn.apache.org/viewcvs/directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBuffer.java?rev=387793&r1=387792&r2=387793&view=diff
==============================================================================
--- directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBuffer.java (original)
+++ directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBuffer.java Wed Mar 22 00:28:53 2006
@@ -381,6 +381,21 @@
     {
         return remaining() > 0;
     }
+    
+    /**
+     * @see java.nio.ByteBuffer#duplicate()
+     */
+    public abstract ByteBuffer duplicate();
+
+    /**
+     * @see java.nio.ByteBuffer#slice()
+     */
+    public abstract ByteBuffer slice();
+    
+    /**
+     * @see java.nio.ByteBuffer#asReadOnlyBuffer()
+     */
+    public abstract ByteBuffer asReadOnlyBuffer();
 
     /**
      * @see java.nio.ByteBuffer#get()

Modified: directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBufferProxy.java
URL: http://svn.apache.org/viewcvs/directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBufferProxy.java?rev=387793&r1=387792&r2=387793&view=diff
==============================================================================
--- directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBufferProxy.java (original)
+++ directory/trunks/mina/core/src/main/java/org/apache/mina/common/ByteBufferProxy.java Wed Mar 22 00:28:53 2006
@@ -607,4 +607,19 @@
     {
         return buf.asOutputStream();
     }
+
+    public ByteBuffer duplicate()
+    {
+        return buf.duplicate();
+    }
+
+    public ByteBuffer slice()
+    {
+        return buf.slice();
+    }
+
+    public ByteBuffer asReadOnlyBuffer()
+    {
+        return buf.asReadOnlyBuffer();
+    }
 }

Modified: directory/trunks/mina/core/src/main/java/org/apache/mina/common/PooledByteBufferAllocator.java
URL: http://svn.apache.org/viewcvs/directory/trunks/mina/core/src/main/java/org/apache/mina/common/PooledByteBufferAllocator.java?rev=387793&r1=387792&r2=387793&view=diff
==============================================================================
--- directory/trunks/mina/core/src/main/java/org/apache/mina/common/PooledByteBufferAllocator.java (original)
+++ directory/trunks/mina/core/src/main/java/org/apache/mina/common/PooledByteBufferAllocator.java Wed Mar 22 00:28:53 2006
@@ -173,9 +173,9 @@
     public ByteBuffer allocate( int capacity, boolean direct )
     {
         ensureNotDisposed();
-        java.nio.ByteBuffer nioBuffer = allocate0( capacity, direct );
+        UnexpandableByteBuffer ubb = allocate0( capacity, direct );
         PooledByteBuffer buf = allocateContainer();
-        buf.init( nioBuffer, true );
+        buf.init( ubb, true );
         return buf;
     }
 
@@ -194,31 +194,36 @@
         return buf;
     }
     
-    private java.nio.ByteBuffer allocate0( int capacity, boolean direct )
+    private UnexpandableByteBuffer allocate0( int capacity, boolean direct )
     {
         ExpiringStack[] bufferStacks = direct? directBufferStacks : heapBufferStacks;
         int idx = getBufferStackIndex( bufferStacks, capacity );
         ExpiringStack stack = bufferStacks[ idx ];
 
-        java.nio.ByteBuffer buf;
+        UnexpandableByteBuffer buf;
         synchronized( stack )
         {
-            buf = ( java.nio.ByteBuffer ) stack.pop();
+            buf = ( UnexpandableByteBuffer ) stack.pop();
         }
 
         if( buf == null )
         {
-            buf = direct ? java.nio.ByteBuffer.allocateDirect( MINIMUM_CAPACITY << idx ) :
-                           java.nio.ByteBuffer.allocate( MINIMUM_CAPACITY << idx );
+            java.nio.ByteBuffer nioBuf = 
+                direct ? java.nio.ByteBuffer.allocateDirect( MINIMUM_CAPACITY << idx ) :
+                java.nio.ByteBuffer.allocate( MINIMUM_CAPACITY << idx );
+            buf = new UnexpandableByteBuffer( nioBuf );
         }
+
+        buf.init();
         
         return buf;
     }
     
-    private void release0( java.nio.ByteBuffer buf )
+    private void release0( UnexpandableByteBuffer buf )
     {
-        ExpiringStack[] bufferStacks = buf.isDirect()? directBufferStacks : heapBufferStacks;
-        ExpiringStack stack = bufferStacks[ getBufferStackIndex( bufferStacks, buf.capacity() ) ];
+        ExpiringStack[] bufferStacks = buf.buf().isDirect()? directBufferStacks : heapBufferStacks;
+        ExpiringStack stack = bufferStacks[ getBufferStackIndex( bufferStacks, buf.buf().capacity() ) ];
+        
         synchronized( stack )
         {
             // push back
@@ -230,7 +235,7 @@
     {
         ensureNotDisposed();
         PooledByteBuffer buf = allocateContainer();
-        buf.init( nioBuffer, false );
+        buf.init( new UnexpandableByteBuffer( nioBuffer ), false );
         buf.setPooled( false );
         return buf;
     }
@@ -337,26 +342,23 @@
 
     private class PooledByteBuffer extends ByteBuffer
     {
-        private java.nio.ByteBuffer buf;
+        private UnexpandableByteBuffer buf;
         private int refCount = 1;
         private boolean autoExpand;
-        private boolean pooled;
-        private long timestamp;
 
         protected PooledByteBuffer()
         {
         }
         
-        private synchronized void init( java.nio.ByteBuffer buf, boolean clear )
+        public synchronized void init( UnexpandableByteBuffer buf, boolean clear )
         {
             this.buf = buf;
             if( clear )
             {
-                buf.clear();
+                buf.buf().clear();
             }
-            buf.order( ByteOrder.BIG_ENDIAN );
+            buf.buf().order( ByteOrder.BIG_ENDIAN );
             autoExpand = false;
-            pooled = true;
             refCount = 1;
         }
         
@@ -394,13 +396,7 @@
                 return;
             }
 
-            if( pooled )
-            {
-                release0( buf );
-            }
-
-            // Update timestamp.
-            timestamp = System.currentTimeMillis();
+            buf.release();
 
             synchronized( containerStack )
             {
@@ -410,17 +406,17 @@
 
         public java.nio.ByteBuffer buf()
         {
-            return buf;
+            return buf.buf();
         }
         
         public boolean isDirect()
         {
-            return buf.isDirect();
+            return buf.buf().isDirect();
         }
         
         public boolean isReadOnly()
         {
-            return buf.isReadOnly();
+            return buf.buf().isReadOnly();
         }
         
         public boolean isAutoExpand()
@@ -436,333 +432,352 @@
         
         public boolean isPooled()
         {
-            return pooled;
+            return buf.isPooled();
         }
         
         public void setPooled( boolean pooled )
         {
-            this.pooled = pooled;
-        }
-
-        public long getTimestamp()
-        {
-            return timestamp;
+            buf.setPooled(pooled);
         }
 
         public int capacity()
         {
-            return buf.capacity();
+            return buf.buf().capacity();
         }
         
         public int position()
         {
-            return buf.position();
+            return buf.buf().position();
         }
 
         public ByteBuffer position( int newPosition )
         {
             autoExpand( newPosition, 0 );
-            buf.position( newPosition );
+            buf.buf().position( newPosition );
             return this;
         }
 
         public int limit()
         {
-            return buf.limit();
+            return buf.buf().limit();
         }
 
         public ByteBuffer limit( int newLimit )
         {
             autoExpand( newLimit, 0 );
-            buf.limit( newLimit );
+            buf.buf().limit( newLimit );
             return this;
         }
 
         public ByteBuffer mark()
         {
-            buf.mark();
+            buf.buf().mark();
             return this;
         }
 
         public ByteBuffer reset()
         {
-            buf.reset();
+            buf.buf().reset();
             return this;
         }
 
         public ByteBuffer clear()
         {
-            buf.clear();
+            buf.buf().clear();
             return this;
         }
 
         public ByteBuffer flip()
         {
-            buf.flip();
+            buf.buf().flip();
             return this;
         }
 
         public ByteBuffer rewind()
         {
-            buf.rewind();
+            buf.buf().rewind();
             return this;
         }
 
         public int remaining()
         {
-            return buf.remaining();
+            return buf.buf().remaining();
+        }
+
+        public ByteBuffer duplicate()
+        {
+            PooledByteBuffer newBuf = allocateContainer();
+            newBuf.init(
+                    new UnexpandableByteBuffer( buf.buf().duplicate(), buf ), false );
+            return newBuf;
+        }
+
+        public ByteBuffer slice()
+        {
+            PooledByteBuffer newBuf = allocateContainer();
+            newBuf.init(
+                    new UnexpandableByteBuffer( buf.buf().slice(), buf ), false );
+            return newBuf;
+        }
+
+        public ByteBuffer asReadOnlyBuffer()
+        {
+            PooledByteBuffer newBuf = allocateContainer();
+            newBuf.init(
+                    new UnexpandableByteBuffer( buf.buf().asReadOnlyBuffer(), buf ), false );
+            return newBuf;
         }
 
         public byte get()
         {
-            return buf.get();
+            return buf.buf().get();
         }
 
         public ByteBuffer put( byte b )
         {
             autoExpand( 1 );
-            buf.put( b );
+            buf.buf().put( b );
             return this;
         }
 
         public byte get( int index )
         {
-            return buf.get( index );
+            return buf.buf().get( index );
         }
 
         public ByteBuffer put( int index, byte b )
         {
             autoExpand( index, 1 );
-            buf.put( index, b );
+            buf.buf().put( index, b );
             return this;
         }
 
         public ByteBuffer get( byte[] dst, int offset, int length )
         {
-            buf.get( dst, offset, length );
+            buf.buf().get( dst, offset, length );
             return this;
         }
 
         public ByteBuffer put( java.nio.ByteBuffer src )
         {
             autoExpand( src.remaining() );
-            buf.put( src );
+            buf.buf().put( src );
             return this;
         }
 
         public ByteBuffer put( byte[] src, int offset, int length )
         {
             autoExpand( length );
-            buf.put( src, offset, length );
+            buf.buf().put( src, offset, length );
             return this;
         }
 
         public ByteBuffer compact()
         {
-            buf.compact();
+            buf.buf().compact();
             return this;
         }
 
         public int compareTo( ByteBuffer that )
         {
-            return this.buf.compareTo( that.buf() );
+            return this.buf.buf().compareTo( that.buf() );
         }
 
         public ByteOrder order()
         {
-            return buf.order();
+            return buf.buf().order();
         }
 
         public ByteBuffer order( ByteOrder bo )
         {
-            buf.order( bo );
+            buf.buf().order( bo );
             return this;
         }
 
         public char getChar()
         {
-            return buf.getChar();
+            return buf.buf().getChar();
         }
 
         public ByteBuffer putChar( char value )
         {
             autoExpand( 2 );
-            buf.putChar( value );
+            buf.buf().putChar( value );
             return this;
         }
 
         public char getChar( int index )
         {
-            return buf.getChar( index );
+            return buf.buf().getChar( index );
         }
 
         public ByteBuffer putChar( int index, char value )
         {
             autoExpand( index, 2 );
-            buf.putChar( index, value );
+            buf.buf().putChar( index, value );
             return this;
         }
 
         public CharBuffer asCharBuffer()
         {
-            return buf.asCharBuffer();
+            return buf.buf().asCharBuffer();
         }
 
         public short getShort()
         {
-            return buf.getShort();
+            return buf.buf().getShort();
         }
 
         public ByteBuffer putShort( short value )
         {
             autoExpand( 2 );
-            buf.putShort( value );
+            buf.buf().putShort( value );
             return this;
         }
 
         public short getShort( int index )
         {
-            return buf.getShort( index );
+            return buf.buf().getShort( index );
         }
 
         public ByteBuffer putShort( int index, short value )
         {
             autoExpand( index, 2 );
-            buf.putShort( index, value );
+            buf.buf().putShort( index, value );
             return this;
         }
 
         public ShortBuffer asShortBuffer()
         {
-            return buf.asShortBuffer();
+            return buf.buf().asShortBuffer();
         }
 
         public int getInt()
         {
-            return buf.getInt();
+            return buf.buf().getInt();
         }
 
         public ByteBuffer putInt( int value )
         {
             autoExpand( 4 );
-            buf.putInt( value );
+            buf.buf().putInt( value );
             return this;
         }
 
         public int getInt( int index )
         {
-            return buf.getInt( index );
+            return buf.buf().getInt( index );
         }
 
         public ByteBuffer putInt( int index, int value )
         {
             autoExpand( index, 4 );
-            buf.putInt( index, value );
+            buf.buf().putInt( index, value );
             return this;
         }
 
         public IntBuffer asIntBuffer()
         {
-            return buf.asIntBuffer();
+            return buf.buf().asIntBuffer();
         }
 
         public long getLong()
         {
-            return buf.getLong();
+            return buf.buf().getLong();
         }
 
         public ByteBuffer putLong( long value )
         {
             autoExpand( 8 );
-            buf.putLong( value );
+            buf.buf().putLong( value );
             return this;
         }
 
         public long getLong( int index )
         {
-            return buf.getLong( index );
+            return buf.buf().getLong( index );
         }
 
         public ByteBuffer putLong( int index, long value )
         {
             autoExpand( index, 8 );
-            buf.putLong( index, value );
+            buf.buf().putLong( index, value );
             return this;
         }
 
         public LongBuffer asLongBuffer()
         {
-            return buf.asLongBuffer();
+            return buf.buf().asLongBuffer();
         }
 
         public float getFloat()
         {
-            return buf.getFloat();
+            return buf.buf().getFloat();
         }
 
         public ByteBuffer putFloat( float value )
         {
             autoExpand( 4 );
-            buf.putFloat( value );
+            buf.buf().putFloat( value );
             return this;
         }
 
         public float getFloat( int index )
         {
-            return buf.getFloat( index );
+            return buf.buf().getFloat( index );
         }
 
         public ByteBuffer putFloat( int index, float value )
         {
             autoExpand( index, 4 );
-            buf.putFloat( index, value );
+            buf.buf().putFloat( index, value );
             return this;
         }
 
         public FloatBuffer asFloatBuffer()
         {
-            return buf.asFloatBuffer();
+            return buf.buf().asFloatBuffer();
         }
 
         public double getDouble()
         {
-            return buf.getDouble();
+            return buf.buf().getDouble();
         }
 
         public ByteBuffer putDouble( double value )
         {
             autoExpand( 8 );
-            buf.putDouble( value );
+            buf.buf().putDouble( value );
             return this;
         }
 
         public double getDouble( int index )
         {
-            return buf.getDouble( index );
+            return buf.buf().getDouble( index );
         }
 
         public ByteBuffer putDouble( int index, double value )
         {
             autoExpand( index, 8 );
-            buf.putDouble( index, value );
+            buf.buf().putDouble( index, value );
             return this;
         }
 
         public DoubleBuffer asDoubleBuffer()
         {
-            return buf.asDoubleBuffer();
+            return buf.buf().asDoubleBuffer();
         }
 
         public ByteBuffer expand( int expectedRemaining )
         {
             if( autoExpand )
             {
-                int pos = buf.position();
-                int limit = buf.limit();
+                int pos = buf.buf().position();
+                int limit = buf.buf().limit();
                 int end = pos + expectedRemaining;
                 if( end > limit ) {
                     ensureCapacity( end );
-                    buf.limit( end );
+                    buf.buf().limit( end );
                 }
             }
             return this;
@@ -772,11 +787,11 @@
         {
             if( autoExpand )
             {
-                int limit = buf.limit();
+                int limit = buf.buf().limit();
                 int end = pos + expectedRemaining;
                 if( end > limit ) {
                     ensureCapacity( end );
-                    buf.limit( end );
+                    buf.buf().limit( end );
                 }
             }
             return this;
@@ -784,31 +799,142 @@
         
         private void ensureCapacity( int requestedCapacity )
         {
-            if( requestedCapacity <= buf.capacity() )
+            if( requestedCapacity <= buf.buf().capacity() )
             {
                 return;
             }
             
+            if( buf.isDerived() )
+            {
+                throw new IllegalStateException( "Derived buffers cannot be expanded." );
+            }
+            
             int newCapacity = MINIMUM_CAPACITY;
             while( newCapacity < requestedCapacity )
             {
                 newCapacity <<= 1;
             }
             
-            java.nio.ByteBuffer oldBuf = this.buf;
-            java.nio.ByteBuffer newBuf = allocate0( newCapacity, isDirect() );
-            newBuf.clear();
-            newBuf.order( oldBuf.order() );
-
-            int pos = oldBuf.position();
-            int limit = oldBuf.limit();
-            oldBuf.clear();
-            newBuf.put( oldBuf );
-            newBuf.position( 0 );
-            newBuf.limit( limit );
-            newBuf.position( pos );
+            UnexpandableByteBuffer oldBuf = this.buf;
+            UnexpandableByteBuffer newBuf = allocate0( newCapacity, isDirect() );
+            newBuf.buf().clear();
+            newBuf.buf().order( oldBuf.buf().order() );
+
+            int pos = oldBuf.buf().position();
+            int limit = oldBuf.buf().limit();
+            oldBuf.buf().clear();
+            newBuf.buf().put( oldBuf.buf() );
+            newBuf.buf().position( 0 );
+            newBuf.buf().limit( limit );
+            newBuf.buf().position( pos );
             this.buf = newBuf;
-            release0( oldBuf );
+            oldBuf.release();
+        }
+    }
+
+    private class UnexpandableByteBuffer
+    {
+        private final java.nio.ByteBuffer buf;
+        private final UnexpandableByteBuffer parentBuf;
+        private int refCount;
+        private boolean pooled;
+
+        protected UnexpandableByteBuffer( java.nio.ByteBuffer buf )
+        {
+            this.buf = buf;
+            this.parentBuf = null;
+        }
+        
+        protected UnexpandableByteBuffer(
+                java.nio.ByteBuffer buf,
+                UnexpandableByteBuffer parentBuf )
+        {
+            parentBuf.acquire();
+            this.buf = buf;
+            this.parentBuf = parentBuf;
+        }
+        
+        public void init()
+        {
+            refCount = 1;
+            pooled = true;
+        }
+        
+        public synchronized void acquire()
+        {
+            if( isDerived() ) {
+                parentBuf.acquire();
+                return;
+            }
+            
+            if( refCount <= 0 )
+            {
+                throw new IllegalStateException( "Already released buffer." );
+            }
+
+            refCount ++;
+        }
+
+        public void release()
+        {
+            if( isDerived() ) {
+                parentBuf.release();
+                return;
+            }
+            
+            synchronized( this )
+            {
+                if( refCount <= 0 )
+                {
+                    refCount = 0;
+                    throw new IllegalStateException(
+                            "Already released buffer.  You released the buffer too many times." );
+                }
+
+                refCount --;
+                if( refCount > 0)
+                {
+                    return;
+                }
+            }
+
+            // No need to return buffers to the pool if it is disposed already.
+            if( disposed )
+            {
+                return;
+            }
+
+            if( pooled )
+            {
+                if( parentBuf != null )
+                {
+                    release0( parentBuf );
+                }
+                else
+                {
+                    release0( this );
+                }
+            }
+        }
+
+        public java.nio.ByteBuffer buf()
+        {
+            return buf;
+        }
+        
+        public boolean isPooled()
+        {
+            return pooled;
+        }
+        
+        public void setPooled( boolean pooled )
+        {
+            this.pooled = pooled;
+        }
+        
+        public boolean isDerived()
+        {
+            return parentBuf != null;
         }
     }
 }

Modified: directory/trunks/mina/core/src/main/java/org/apache/mina/common/SimpleByteBufferAllocator.java
URL: http://svn.apache.org/viewcvs/directory/trunks/mina/core/src/main/java/org/apache/mina/common/SimpleByteBufferAllocator.java?rev=387793&r1=387792&r2=387793&view=diff
==============================================================================
--- directory/trunks/mina/core/src/main/java/org/apache/mina/common/SimpleByteBufferAllocator.java (original)
+++ directory/trunks/mina/core/src/main/java/org/apache/mina/common/SimpleByteBufferAllocator.java Wed Mar 22 00:28:53 2006
@@ -52,12 +52,12 @@
         {
             nioBuffer = java.nio.ByteBuffer.allocate( capacity );            
         }
-        return new SimpleByteBuffer( nioBuffer, true );
+        return new SimpleByteBuffer( nioBuffer );
     }
     
     public ByteBuffer wrap( java.nio.ByteBuffer nioBuffer )
     {
-        return new SimpleByteBuffer( nioBuffer, false );
+        return new SimpleByteBuffer( nioBuffer );
     }
 
     private static class SimpleByteBuffer extends ByteBuffer
@@ -66,13 +66,9 @@
         private int refCount = 1;
         private boolean autoExpand;
 
-        protected SimpleByteBuffer( java.nio.ByteBuffer buf, boolean clear )
+        protected SimpleByteBuffer( java.nio.ByteBuffer buf )
         {
             this.buf = buf;
-            if( clear )
-            {
-                buf.clear();
-            }
             buf.order( ByteOrder.BIG_ENDIAN );
             autoExpand = false;
             refCount = 1;
@@ -510,6 +506,18 @@
             newBuf.limit( limit );
             newBuf.position( pos );
             this.buf = newBuf;
+        }
+
+        public ByteBuffer duplicate() {
+            return new SimpleByteBuffer( this.buf.duplicate() );
+        }
+
+        public ByteBuffer slice() {
+            return new SimpleByteBuffer( this.buf.slice() );
+        }
+
+        public ByteBuffer asReadOnlyBuffer() {
+            return new SimpleByteBuffer( this.buf.asReadOnlyBuffer() );
         }
     }
 }

Modified: directory/trunks/mina/core/src/test/java/org/apache/mina/common/ByteBufferTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/mina/core/src/test/java/org/apache/mina/common/ByteBufferTest.java?rev=387793&r1=387792&r2=387793&view=diff
==============================================================================
--- directory/trunks/mina/core/src/test/java/org/apache/mina/common/ByteBufferTest.java (original)
+++ directory/trunks/mina/core/src/test/java/org/apache/mina/common/ByteBufferTest.java Wed Mar 22 00:28:53 2006
@@ -19,6 +19,7 @@
 package org.apache.mina.common;
 
 import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
@@ -630,6 +631,148 @@
             Assert.fail();
         }
         catch( IllegalStateException e )
+        {
+            // OK
+        }
+    }
+    
+    public void testDuplicate() throws Exception
+    {
+        java.nio.ByteBuffer nioBuf;
+        ByteBuffer original;
+        ByteBuffer duplicate;
+        
+        // Test if the buffer is duplicated correctly.
+        original = ByteBuffer.allocate( 16 ).sweep();
+        nioBuf = original.buf();
+        original.position( 4 );
+        original.limit( 10 );
+        duplicate = original.duplicate();
+        original.put( 4, ( byte ) 127 );
+        Assert.assertEquals( 4, duplicate.position() );
+        Assert.assertEquals( 10, duplicate.limit() );
+        Assert.assertEquals( 16, duplicate.capacity() );
+        Assert.assertNotSame( original.buf(), duplicate.buf() );
+        Assert.assertEquals( 127, duplicate.get( 4 ) );
+        original.release();
+        duplicate.release();
+
+        //// Check if pooled correctly.
+        original = ByteBuffer.allocate( 16 );
+        Assert.assertSame( nioBuf, original.buf() );
+        original.release();
+        
+        // Try to release duplicate first.
+        original = ByteBuffer.allocate( 16 );
+        duplicate = original.duplicate();
+        duplicate.release();
+        original.release();
+
+        //// Check if pooled correctly.
+        original = ByteBuffer.allocate( 16 );
+        Assert.assertSame( nioBuf, original.buf() );
+        original.release();
+        
+        // Test a duplicate of a duplicate.
+        original = ByteBuffer.allocate( 16 );
+        duplicate = original.duplicate();
+        ByteBuffer anotherDuplicate = duplicate.duplicate();
+        anotherDuplicate.release();
+        original.release();
+        duplicate.release();
+        try
+        {
+            duplicate.release();
+            Assert.fail();
+        }
+        catch( IllegalStateException e )
+        {
+            // OK
+        }
+        try
+        {
+            anotherDuplicate.release();
+            Assert.fail();
+        }
+        catch( IllegalStateException e )
+        {
+            // OK
+        }
+        
+        //// Check if pooled correctly.
+        original = ByteBuffer.allocate( 16 );
+        Assert.assertSame( nioBuf, original.buf() );
+        original.release();
+
+        
+        
+        // Try to expand.
+        try
+        {
+            original = ByteBuffer.allocate( 16 );
+            duplicate = original.duplicate();
+            duplicate.setAutoExpand( true );
+            duplicate.putString(
+                    "A very very very very looooooong string",
+                    Charset.forName( "ISO-8859-1" ).newEncoder() );
+            Assert.fail();
+        }
+        catch( IllegalStateException e )
+        {
+            // OK
+        }
+    }
+    
+    public void testSlice() throws Exception
+    {
+        ByteBuffer original;
+        ByteBuffer slice;
+        
+        // Test if the buffer is sliced correctly.
+        original = ByteBuffer.allocate( 16 ).sweep();
+        original.position( 4 );
+        original.limit( 10 );
+        slice = original.slice();
+        original.put( 4, ( byte ) 127 );
+        Assert.assertEquals( 0, slice.position() );
+        Assert.assertEquals( 6, slice.limit() );
+        Assert.assertEquals( 6, slice.capacity() );
+        Assert.assertNotSame( original.buf(), slice.buf() );
+        Assert.assertEquals( 127, slice.get( 0 ) );
+        original.release();
+        slice.release();
+    }
+
+    public void testReadOnlyBuffer() throws Exception
+    {
+        ByteBuffer original;
+        ByteBuffer duplicate;
+        
+        // Test if the buffer is duplicated correctly.
+        original = ByteBuffer.allocate( 16 ).sweep();
+        original.position( 4 );
+        original.limit( 10 );
+        duplicate = original.asReadOnlyBuffer();
+        original.put( 4, ( byte ) 127 );
+        Assert.assertEquals( 4, duplicate.position() );
+        Assert.assertEquals( 10, duplicate.limit() );
+        Assert.assertEquals( 16, duplicate.capacity() );
+        Assert.assertNotSame( original.buf(), duplicate.buf() );
+        Assert.assertEquals( 127, duplicate.get( 4 ) );
+        original.release();
+        duplicate.release();
+
+        // Try to expand.
+        try
+        {
+            original = ByteBuffer.allocate( 16 );
+            duplicate = original.asReadOnlyBuffer();
+            duplicate.putString(
+                    "A very very very very looooooong string",
+                    Charset.forName( "ISO-8859-1" ).newEncoder() );
+            Assert.fail();
+        }
+        catch( ReadOnlyBufferException e )
         {
             // OK
         }