/* * Created on Apr 21, 2004 * Created by Alon Rohter * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package org.gudy.azureus2.core3.util; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SocketChannel; //import java.util.*; /** * Virtual direct byte buffer given out and tracker * by the buffer pool. */ public class DirectByteBuffer { // allocator constants public static final byte AL_NONE = 0; public static final byte AL_EXTERNAL = 1; public static final byte AL_OTHER = 2; public static final byte AL_PT_READ = 3; public static final byte AL_PT_LENGTH = 4; public static final byte AL_CACHE_READ = 5; public static final byte AL_DM_READ = 6; public static final byte AL_DM_ZERO = 7; public static final byte AL_DM_CHECK = 8; public static final byte AL_BT_PIECE = 9; public static final byte AL_CACHE_WRITE = 10; public static final byte AL_PROXY_RELAY = 11; public static final byte AL_MSG = 12; public static final byte AL_MSG_AZ_HAND = 13; public static final byte AL_MSG_AZ_PEX = 14; public static final byte AL_MSG_BT_CANCEL = 15; public static final byte AL_MSG_BT_HAND = 16; public static final byte AL_MSG_BT_HAVE = 17; public static final byte AL_MSG_BT_PIECE = 18; public static final byte AL_MSG_BT_REQUEST = 19; public static final byte AL_MSG_BT_KEEPALIVE = 20; public static final byte AL_MSG_BT_HEADER = 21; public static final byte AL_MSG_AZ_HEADER = 22; public static final byte AL_MSG_BT_PAYLOAD = 23; public static final byte AL_MSG_AZ_PAYLOAD = 24; public static final byte AL_FILE = 25; public static final byte AL_NET_CRYPT = 26; public static final byte AL_MSG_LT_EXT_MESSAGE = 27; public static final byte AL_MSG_LT_HANDSHAKE = 28; public static final byte AL_MSG_UT_PEX = 29; public static final byte AL_MSG_BT_DHT_PORT = 30; public static final String[] AL_DESCS = { "None", "Ext", "Other", "PeerRead", "PeerLen", "CacheRead", "DiskRead", "DiskZero", "DiskCheck", "BTPiece", "CacheWrite", "ProxyRelay", "Messaging", "AZHandshake", "AZPEX", "BTCancel", "BTHandshake", "BTHave", "BTPiece", "BTRequest", "BTKeepAlive", "BTHeader", "AZHeader", "BTPayload", "AZPayload", "File", "MsgCrypt", "LTExtMsg","LTExtHandshake","UTPEX", "BTDHTPort"}; // subsystem ids public static final byte SS_NONE = 0; // not used, required to id cycled buffers public static final byte SS_EXTERNAL = 1; public static final byte SS_OTHER = 2; public static final byte SS_CACHE = 3; public static final byte SS_FILE = 4; public static final byte SS_NET = 5; public static final byte SS_BT = 6; public static final byte SS_DR = 7; public static final byte SS_DW = 8; public static final byte SS_PEER = 9; public static final byte SS_PROXY = 10; public static final byte SS_MSG = 11; public static final String[] SS_DESCS = { "None", "Ext", "Other", "Cache", "File", "Net", "BT", "DiskRead", "DiskWrite", "Peer", "Proxy", "Messaging" }; public static final byte OP_LIMIT = 0; public static final byte OP_LIMIT_INT = 1; public static final byte OP_POSITION = 2; public static final byte OP_POSITION_INT = 3; public static final byte OP_CLEAR = 4; public static final byte OP_FLIP = 5; public static final byte OP_REMANING = 6; public static final byte OP_CAPACITY = 7; public static final byte OP_PUT_BYTEARRAY = 8; public static final byte OP_PUT_DBB = 9; public static final byte OP_PUT_BB = 10; public static final byte OP_PUTINT = 11; public static final byte OP_PUT_BYTE = 12; public static final byte OP_GET = 13; public static final byte OP_GET_INT = 14; public static final byte OP_GET_BYTEARRAY = 15; public static final byte OP_GETINT = 16; public static final byte OP_GETINT_INT = 17; public static final byte OP_HASREMAINING = 18; public static final byte OP_READ_FC = 19; public static final byte OP_WRITE_FC = 20; public static final byte OP_READ_SC = 21; public static final byte OP_WRITE_SC = 22; public static final byte OP_GETBUFFER = 23; public static final byte OP_GETSHORT = 24; public static final byte OP_PUTSHORT = 25; public static final String[] OP_DESCS = { "limit", "limit(int)", "position", "position(int)", "clear", "flip", "remaining", "capacity", "put(byte[])", "put(dbb)", "put(bbb)", "putInt", "put(byte)", "get", "get(int)", "get(byte[])", "getInt", "getInt(int", "hasRemaining", "read(fc)", "write(fc)", "read(sc)", "write(sc)", "getBuffer", "getShort", "putShort", }; // protected static final boolean TRACE = AEDiagnostics.TRACE_DIRECT_BYTE_BUFFERS; protected static final boolean TRACE = true; protected static final int TRACE_BUFFER_SIZE = 64; // must be even private ByteBuffer buffer; private DirectByteBufferPool pool; private byte allocator; private boolean was_returned_to_pool = false; //private byte[] trace_buffer; //private int trace_buffer_pos; public DirectByteBuffer( ByteBuffer _buffer ) { this( AL_NONE, _buffer, null ); } public DirectByteBuffer( byte _allocator, ByteBuffer _buffer, DirectByteBufferPool _pool ) { if (_buffer == null) {throw new NullPointerException("buffer is null");} allocator = _allocator; buffer = _buffer; pool = _pool; if ( TRACE ){ /* trace_buffer = new byte[TRACE_BUFFER_SIZE]; Arrays.fill(trace_buffer,(byte)0); */ } } /** * constructor for reference counted version * @param basis */ protected DirectByteBuffer( DirectByteBuffer basis ) { allocator = basis.allocator; buffer = basis.buffer; pool = null; if (buffer == null) {throw new NullPointerException("basis.buffer is null");} } public ReferenceCountedDirectByteBuffer getReferenceCountedBuffer() { ReferenceCountedDirectByteBuffer res = new ReferenceCountedDirectByteBuffer( this ); return( res ); } protected void traceUsage( byte subsystem, byte operation ) { if ( TRACE ){ /* trace_buffer[trace_buffer_pos++] = subsystem; trace_buffer[trace_buffer_pos++] = operation; if ( trace_buffer_pos == TRACE_BUFFER_SIZE ){ trace_buffer_pos = 0; } */ } } protected String getTraceString() { if ( TRACE ){ /* StringBuffer sb = new StringBuffer(); sb.append( AL_DESCS[allocator]); sb.append( ":" ); boolean wrapped = false; int start = 0; int end = trace_buffer_pos; if ( trace_buffer[trace_buffer_pos] != 0 ){ start = trace_buffer_pos; wrapped = true; } if ( end == 0 && !wrapped ){ sb.append( "not used"); }else{ if ( wrapped ){ sb.append( "*" ); } int num = 0; do{ if ( num++ > 0 ){ sb.append(","); } sb.append( SS_DESCS[trace_buffer[start++]]); sb.append( "/" ); sb.append( OP_DESCS[trace_buffer[start++]]); if ( start == TRACE_BUFFER_SIZE ){ start = 0; } }while( start != end ); } return( sb.toString()); */ } return( null ); } protected void dumpTrace( Throwable e ) { if ( TRACE ){ System.out.println( getTraceString()); // Ignore.ignore(e); } } protected ByteBuffer getBufferInternal() { return( buffer ); } protected byte getAllocator() { return( allocator ); } // **** accessor methods **** public int limit( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_LIMIT ); } return( buffer.limit()); } public void limit( byte subsystem, int l ) { if ( TRACE ){ traceUsage( subsystem, OP_LIMIT_INT ); } if (buffer == null) { System.out.println("Trying to limit null buffer - returned? " + this.was_returned_to_pool); } buffer.limit(l); } public int position( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_POSITION); } return( buffer.position()); } public void position( byte subsystem, int l ) { if ( TRACE ){ traceUsage( subsystem, OP_POSITION_INT); } buffer.position(l); } public void clear( byte subsystem) { if ( TRACE ){ traceUsage( subsystem, OP_CLEAR ); } buffer.clear(); } public void flip( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_FLIP ); } buffer.flip(); } public int remaining( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_REMANING ); } return( buffer.remaining()); } public int capacity( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_CAPACITY ); } return( buffer.capacity()); } public void put( byte subsystem, byte[] data ) { if ( TRACE ){ traceUsage( subsystem, OP_PUT_BYTEARRAY ); } buffer.put( data ); } public void put( byte subsystem, byte[] data, int offset, int length ) { if ( TRACE ){ traceUsage( subsystem, OP_PUT_BYTEARRAY ); } buffer.put( data, offset, length ); } public void put( byte subsystem, DirectByteBuffer data ) { if ( TRACE ){ traceUsage( subsystem, OP_PUT_DBB); } buffer.put( data.buffer ); } public void put( byte subsystem, ByteBuffer data ) { if ( TRACE ){ traceUsage( subsystem, OP_PUT_BB); } buffer.put( data ); } public void put( byte subsystem, byte data ) { if ( TRACE ){ traceUsage( subsystem, OP_PUT_BYTE ); } buffer.put( data ); } public void putShort( byte subsystem, short x ) { if ( TRACE ){ traceUsage( subsystem, OP_PUTSHORT); } buffer.putShort( x ); } public void putInt( byte subsystem, int data ) { if ( TRACE ){ traceUsage( subsystem, OP_PUTINT); } buffer.putInt( data ); } public byte get( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_GET); } return( buffer.get()); } public byte get( byte subsystem, int x ) { if ( TRACE ){ traceUsage( subsystem, OP_GET_INT); } return( buffer.get(x)); } public void get( byte subsystem, byte[] data ) { if ( TRACE ){ traceUsage( subsystem, OP_GET_BYTEARRAY); } buffer.get(data); } public short getShort( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_GETSHORT); } return( buffer.getShort()); } public int getInt( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_GETINT); } return( buffer.getInt()); } public int getInt( byte subsystem, int x ) { if ( TRACE ){ traceUsage( subsystem, OP_GETINT_INT ); } return( buffer.getInt(x)); } public boolean hasRemaining( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_HASREMAINING ); } return( buffer.hasRemaining()); } public int read( byte subsystem, FileChannel chan ) throws IOException { if ( TRACE ){ traceUsage( subsystem, OP_READ_FC); } try{ return( chan.read(buffer )); }catch( IllegalArgumentException e ){ dumpTrace(e); throw( e ); } } public int write( byte subsystem, FileChannel chan ) throws IOException { if ( TRACE ){ traceUsage( subsystem, OP_WRITE_FC ); } try{ return( chan.write(buffer )); }catch( IllegalArgumentException e ){ dumpTrace(e); throw( e ); } } public int read( byte subsystem, SocketChannel chan ) throws IOException { if ( TRACE ){ traceUsage( subsystem, OP_READ_SC ); } try{ return( chan.read(buffer )); }catch( IllegalArgumentException e ){ dumpTrace(e); throw( e ); } } public int write( byte subsystem, SocketChannel chan ) throws IOException { if ( TRACE ){ traceUsage( subsystem, OP_WRITE_SC ); } try{ return( chan.write(buffer )); }catch( IllegalArgumentException e ){ dumpTrace(e); throw( e ); } } public ByteBuffer getBuffer( byte subsystem ) { if ( TRACE ){ traceUsage( subsystem, OP_GETBUFFER ); } return( buffer ); } public void returnToPool() { if ( pool != null ){ // we can't afford to return a buffer more than once to the pool as it'll get // handed out twice in parallel and cause weird problems. We haven't been able // to totally eliminiate duplicate returnToPool calls.... synchronized( this ){ if ( buffer == null ){ // Debug.out( "Buffer already returned to pool"); }else{ pool.returnBufferSupport( this ); buffer = null; was_returned_to_pool = true; } } } } /** * Normally you should know when a buffer is/isn't free and NOT CALL THIS METHOD * However, there are some error situations where the existing code doesn't correctly * manage things - we know this and don't want spurious logs occuring as per the above * normal method */ public void returnToPoolIfNotFree() { if ( pool != null ){ // we can't afford to return a buffer more than once to the pool as it'll get // handed out twice in parallel and cause weird problems. We haven't been able // to totally eliminiate duplicate returnToPool calls.... synchronized( this ){ if ( buffer != null ){ pool.returnBufferSupport( this ); buffer = null; was_returned_to_pool = true; } } } } }