FileDocCategorySizeDatePackage
BDecoder.javaAPI DocAzureus 3.0.3.412233Fri Aug 31 14:26:40 BST 2007org.gudy.azureus2.core3.util

BDecoder

public class BDecoder extends Object
A set of utility methods to decode a bencoded array of byte into a Map. integer are represented as Long, String as byte[], dictionnaries as Map, and list as List.
author
TdC_VgA

Fields Summary
private boolean
recovery_mode
Constructors Summary
public BDecoder()

	
  
Methods Summary
public static java.util.Mapdecode(byte[] data)

		return( new BDecoder().decodeByteArray( data ));
	
public static java.util.Mapdecode(java.io.BufferedInputStream is)

		return( new BDecoder().decodeStream( is ));
	
private java.util.Mapdecode(java.io.ByteArrayInputStream data)

      Object res = decodeInputStream(data, 0);
      
      if ( res == null ){
    	  
    	  throw( new BEncodingException( "BDecoder: zero length file" ));
    	  
      }else if ( !(res instanceof Map )){
    	  
    	  throw( new BEncodingException( "BDecoder: top level isn't a Map" ));
      }
      
      return((Map)res );
  
public java.util.MapdecodeByteArray(byte[] data)

 
    return( decode(new ByteArrayInputStream(data)));
  
private java.lang.ObjectdecodeInputStream(java.io.InputStream bais, int nesting)

    if (nesting == 0 && !bais.markSupported()) {
    	
      throw new IOException("InputStream must support the mark() method");
    }

    //set a mark
    bais.mark(Integer.MAX_VALUE);

    //read a byte
    int tempByte = bais.read();

    //decide what to do
    switch (tempByte) {
      case 'd" :
        //create a new dictionary object
        Map tempMap = new HashMap();

        try{
	        //get the key   
	        byte[] tempByteArray = null;
	        
	        while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting+1)) != null) {
	        	
	        	//decode some more
	        	
	          Object value = decodeInputStream(bais,nesting+1);

	          	// keys often repeat a lot - intern to save space

	          String	key = StringInterner.intern( tempByteArray );
	          
	          if ( key == null ){
	        	 	          
	        	  CharBuffer	cb = Constants.BYTE_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
	          
	        	  key = new String(cb.array(),0,cb.limit());
	          
	        	  key = StringInterner.intern( key );
	          }
	          
	          tempMap.put( key, value);
	        }
	
	        bais.mark(Integer.MAX_VALUE);
	        tempByte = bais.read();
	        bais.reset();
	        if ( nesting > 0 && tempByte == -1 ){
	        		        		
	        	throw( new BEncodingException( "BDecoder: invalid input data, 'e' missing from end of dictionary"));
	        }
        }catch( Throwable e ){
        	
        	if ( !recovery_mode ){

        		if ( e instanceof IOException ){
        			
        			throw((IOException)e);
        		}
        		
        		throw( new IOException( Debug.getNestedExceptionMessage(e)));
        	}
        }
        
        //return the map
        return tempMap;

      case 'l" :
        //create the list
        List tempList = new ArrayList();

        try{
	        //create the key
	        Object tempElement = null;
	        while ((tempElement = decodeInputStream(bais, nesting+1)) != null) {
	          //add the element
	          tempList.add(tempElement);
	        }
	        
	        bais.mark(Integer.MAX_VALUE);
	        tempByte = bais.read();
	        bais.reset();
	        if ( nesting > 0 && tempByte == -1 ){
	        		        		
	        	throw( new BEncodingException( "BDecoder: invalid input data, 'e' missing from end of list"));
	        }
       }catch( Throwable e ){
        	
        	if ( !recovery_mode ){

        		if ( e instanceof IOException ){
        			
        			throw((IOException)e);
        		}
        		
        		throw( new IOException( Debug.getNestedExceptionMessage(e)));
        	}
        }
               //return the list
        return tempList;

      case 'e" :
      case -1 :
        return null;

      case 'i" :
        return new Long(getNumberFromStream(bais, 'e"));

      case '0" :
      case '1" :
      case '2" :
      case '3" :
      case '4" :
      case '5" :
      case '6" :
      case '7" :
      case '8" :
      case '9" :
        //move back one
        bais.reset();
        //get the string
        return getByteArrayFromStream(bais);

      default :{
    	  
    	  int	rem_len = bais.available();
    	  
    	  if ( rem_len > 256 ){
    		  
    		  rem_len	= 256;
    	  }
    	  
    	  byte[] rem_data = new byte[rem_len];
    	  
    	  bais.read( rem_data );
    	  
    	  throw( new BEncodingException(
        	"BDecoder: unknown command '" + tempByte + ", remainder = " + new String( rem_data )));
      }
    }
  
public java.util.MapdecodeStream(java.io.BufferedInputStream data)

      Object	res = decodeInputStream(data, 0);
      
      if ( res == null ){
    	  
    	  throw( new BEncodingException( "BDecoder: zero length file" ));
    	  
      }else if ( !(res instanceof Map )){
    	  
    	  throw( new BEncodingException( "BDecoder: top level isn't a Map" ));
      }
      
      return((Map)res );
  
public static java.util.MapdecodeStrings(java.util.Map map)
Converts any byte[] entries into UTF-8 strings

param
map
return

	  if (map == null ){
		  
		  return( null );
	  }
	  
	  Iterator it = map.entrySet().iterator();
	  
	  while( it.hasNext()){
		  
		  Map.Entry	entry = (Map.Entry)it.next();
		  
		  Object	value = entry.getValue();
		  
		  if ( value instanceof byte[]){
			  
			  try{
				  entry.setValue( new String((byte[])value,"UTF-8" ));
				  
			  }catch( Throwable e ){
				  
				  Debug.printStackTrace(e);
			  }
		  }else if ( value instanceof Map ){
			  
			  decodeStrings((Map)value );
		  }else if ( value instanceof List ){
			  
			  decodeStrings((List)value );
		  }
	  }
	  
	  return( map );
  
public static java.util.ListdecodeStrings(java.util.List list)

	  if ( list == null ){
		  
		  return( null );
	  }
	  
	  for (int i=0;i<list.size();i++){
		  
		  Object value = list.get(i);
		  
		  if ( value instanceof byte[]){
			  
			  try{
				  String str = new String((byte[])value, "UTF-8" );
				  
				  list.set( i, str );
			  
			  }catch( Throwable e ){
				  
				  Debug.printStackTrace(e);
			  }
		  }else if ( value instanceof Map ){
			  
			  decodeStrings((Map)value );
			  
		  }else if ( value instanceof List ){
			  
			  decodeStrings((List)value );		 
		  }
	  }
	  
	  return( list );
  
private byte[]getByteArrayFromStream(java.io.InputStream bais)

    int length = (int) getNumberFromStream(bais, ':");

    if (length < 0) {
      return null;
    }
    
    	// note that torrent hashes can be big (consider a 55GB file with 2MB pieces
    	// this generates a pieces hash of 1/2 meg
    
    if ( length > 8*1024*1024 ){
    	
    	throw( new IOException( "Byte array length too large (" + length + ")"));
    }
    
    byte[] tempArray = new byte[length];
    int count = 0;
    int len = 0;
    //get the string
    while (count != length && (len = bais.read(tempArray, count, length - count)) > 0) {
      count += len;
    }

    if ( count != tempArray.length ){
     	throw( new IOException( "BDecoder::getByteArrayFromStream: truncated"));
    }
    
    return tempArray;
  
private longgetNumberFromStream(java.io.InputStream bais, char parseChar)

    StringBuffer sb = new StringBuffer(3);

    int tempByte = bais.read();
    while ((tempByte != parseChar) && (tempByte >= 0)) {
    	sb.append((char)tempByte);
      tempByte = bais.read();
    }

    //are we at the end of the stream?
    if (tempByte < 0) {
      return -1;
    }

    String str = sb.toString();
    
    	// support some borked impls that sometimes don't bother encoding anything
    
    if ( str.length() == 0 ){
    	
    	return( 0 );
    }
    
    return Long.parseLong(str);
  
public static voidmain(java.lang.String[] args)

	  
	  print( 	new File( "C:\\Temp\\xxx.torrent" ),
			  	new File( "C:\\Temp\\xxx.txt" ));
  
private voidprint(java.io.PrintWriter writer, java.lang.Object obj)

	  print( writer, obj, "", false );
  
private voidprint(java.io.PrintWriter writer, java.lang.Object obj, java.lang.String indent, boolean skip_indent)

	  String	use_indent = skip_indent?"":indent;
	  
	  if ( obj instanceof Long ){
		  
		  writer.println( use_indent + obj );
		  
	  }else if ( obj instanceof byte[]){
		  
		  byte[]	b = (byte[])obj;
		  
		  if ( b.length==20 ){
			  writer.println( use_indent + " { "+ ByteFormatter.nicePrint( b )+ " }" );
		  }else if ( b.length < 64 ){
			  writer.println( new String(b) );
		  }else{
			  writer.println( "[byte array length " + b.length );
		  }
		
	  }else if ( obj instanceof String ){
		  
		  writer.println( use_indent + obj );

	  }else if ( obj instanceof List ){
		  
		  List	l = (List)obj;
		  
		  writer.println( use_indent + "[" );
		  
		  for (int i=0;i<l.size();i++){
			
			  writer.print( indent + "  (" + i + ") " );
			  
			  print( writer, l.get(i), indent + "    ", true );
		  }
		  
		  writer.println( indent + "]" );

	  }else{
		  
		  Map	m = (Map)obj;
		  
		  Iterator	it = m.keySet().iterator();
		  
		  while( it.hasNext()){
			  
			  String	key = (String)it.next();
			  
			  if ( key.length() > 256 ){
				  writer.print( indent + key.substring(0,256) + "... = " );
			  }else{
				  writer.print( indent + key + " = " );
			  }
			  
			  print( writer, m.get(key), indent + "  ", true );
		  }
	  }
  
private static voidprint(java.io.File f, java.io.File output)

	  try{
		  BDecoder	decoder = new BDecoder();
		  
		  decoder.setRecoveryMode( false );
		  
		  PrintWriter	pw = new PrintWriter( new FileWriter( output ));
		  
		  decoder.print( pw, decoder.decodeStream( new BufferedInputStream( new FileInputStream( f ))));
		  
		  pw.flush();
		  
	  }catch( Throwable e ){
		  
		  e.printStackTrace();
	  }
  
public voidsetRecoveryMode(boolean r)

	  recovery_mode	= r;