• Welcome to Valhalla Legends Archive.
 

Buffer

Started by iago, January 01, 2004, 06:15 AM

Previous topic - Next topic

iago

I can't figure out where I posted this (maybe I didn't?) but anyway, here it is (it is probably different/updated from what I posted before):

// Buffer.java
// by iago
// This is going to attempt to implement a binary packet buffer
// in Java.  How well this will work, of course, remains to be seen :-/

import java.io.*;

public class Buffer
{
   public static void main(String args[])
   {
       // testing stuff goes here
   }


   protected byte[] buffer;
   protected int currentLength;
   protected int maxLength;
   protected int incrementer;
   public static final int DEFAULT_INCREMENT = 128;


   public Buffer()
   {
       currentLength = 0;
       maxLength = DEFAULT_INCREMENT;
       incrementer = DEFAULT_INCREMENT;
       buffer = new byte[maxLength];
   }

   // Creates a buffer with a specific initialLength, which should
   // be rather high, higher than the average length.  This will
   // also work as the incrementer
   public Buffer(int initialLength)
   {
       currentLength = 0;
       maxLength = initialLength;
       incrementer = initialLength;
       buffer = new byte[maxLength];
   }
   
   // Returns the number of bytes in the buffer
   public int length()
   {
       return currentLength;
   }

   // Verifies that the buffer is long enough to accomidate the data
   // being inserted, and expands it if it isn't
   public void verifyLength(int newBytes)
   {
       int minSize = currentLength + newBytes;
       // Check to make sure we have enough room
       if(minSize > maxLength)
       {
           // Increment until we have a usable size
           while(minSize > maxLength)
           {
               maxLength += incrementer;
               //System.out.println("Min size: " + minSize + "  New buffer length: " + maxLength);
           }

           // Back up the old buffer so we can make a copy of it
           byte[] oldBuffer = buffer;
           // Create a new buffer of the appropriate length
           buffer = new byte[maxLength];

           for(int i = 0; i < currentLength; i++)
           {
               buffer[i] = oldBuffer[i];
           }
       }
   }

   // Add a 4 byte DWord to the buffer
   public void addDWord(long DWord)
   {
       verifyLength(4);

       buffer[currentLength++] = (byte)((DWord & 0x000000FF) >> 0);
       buffer[currentLength++] = (byte)((DWord & 0x0000FF00) >> 8);
       buffer[currentLength++] = (byte)((DWord & 0x00FF0000) >> 16);
       buffer[currentLength++] = (byte)((DWord & 0xFF000000) >> 24);
   }
   
   // Add a 2 byte Word to the buffer
   // (will only add the right two bytes if it overflows
   public void addWord(int Word)
   {
       verifyLength(2);
       
       buffer[currentLength++] = (byte) ((Word & 0x00FF) >> 0);
       buffer[currentLength++] = (byte) ((Word & 0xFF00) >> 8);
   }
   
   // Add a single byte to the buffer
   public void addByte(int Byte)
   {
       verifyLength(1);
       buffer[currentLength++] = (byte) (Byte & 0xFF);
   }

   public void addNTString(String str)
   {
       verifyLength(str.length() + 1); // +1 for the null

       for(int i = 0; i < str.length(); i++)
       {
           buffer[currentLength++] = (byte)str.charAt(i);
       }
       
       buffer[currentLength++] = (byte) 0;
   }
       
   public void addNonNTString(String str)
   {
       verifyLength(str.length());
           
       for(int i = 0; i < str.length(); i++)
       {
           buffer[currentLength++] = (byte)str.charAt(i);
       }  
   }

   // This will return a string object which might look like this:
   // FF 03 05 11 12 13 14 15 15 16 12 45 BC 46 48 FF    ...........D...
   // 69 FF FF FF                                        i...
   public String toText()
   {
       StringBuffer returnString = new StringBuffer( (currentLength * 3) + // The hex
                                                     (currentLength) +     // The ascii
                                                     (currentLength / 4) + // The tabs/\n's
                                                     30 );                 // The text
       
       returnString.append("Buffer contents:\n");
       int i, j; // Loop variables
       for(i = 0; i < currentLength; i++)
       {
           if((i != 0) && (i % 16 == 0))
           {
               // If it's a multiple of 16 and i isn't null, show the ascii
               returnString.append('\t');
               for(j = i - 16; j < i; j++)
               {
                   if(buffer[j] < 0x20 || buffer[j] > 0x7F)
                       returnString.append('.');
                   else
                       returnString.append((char)buffer[j]);
               }
               // Add a linefeed after the string
               returnString.append("\n");
           }
   
           returnString.append(Integer.toString((buffer[i] & 0xF0) >> 4, 16) +
                   Integer.toString((buffer[i] & 0x0F) >> 0, 16));
           returnString.append(' ');
       }
       
       // Add padding spaces if it's not a multiple of 16
       if(i != 0 && i % 16 != 0)
       {
           for(j = 0; j < ((16 - (i % 16)) * 3); j++)
           {
               returnString.append(' ');
           }
       }
       // Add the tab for alignment
       returnString.append('\t');
   
       // Add final chararacters at right, after padding
   
       // If it was at the end of a line, print out the full line
       if(i > 0 && (i % 16) == 0)
       {
           j = i - 16;
       }    
       else
       {
           j = (i - (i % 16));
       }
   
       for(; i >= 0 && j < i; j++)
       {
           if(buffer[j] < 0x20 || buffer[j] > 0x7F)
               returnString.append('.');
           else
               returnString.append((char) buffer[j]);
       }
   
       // Finally, tidy it all up with a newline
       returnString.append('\n');
       returnString.append("Length: " + currentLength + '\n');
   
       return returnString.toString();
   }

   // These overloaded add functions just call the other functions
   public void add(byte Byte)
   {
       addByte(Byte);
   }
   public void add(short Word)
   {
       addWord(Word);
   }
   public void add(long DWord)
   {
       addDWord(DWord);
   }
   public void add(String str)
   {
       addNTString(str);
   }
   
   // Empty the buffer
   public void clear()
   {
       currentLength = 0;
   }
   
   
   // Now we need some functions for taking apart the buffer.
   
   // I'll implement ByteAt, WordAt, and DWordAt for now
   public byte byteAt(int location)
   {
       return buffer[location];
   }
   public short wordAt(int location)
   {
       int retVal = (((short)buffer[location++] << 0) & 0x000000FF) |
                      (((short)buffer[location++] << 8) & 0x0000FF00);
       
       return (short)retVal;
   }
   public int dwordAt(int location)
   {
       return (((int)buffer[location++] << 0) & 0x000000FF) |
              (((int)buffer[location++] << 8) & 0x0000FF00) |
              (((int)buffer[location++] << 16) & 0x00FF0000) |
              (((int)buffer[location++] << 24) & 0xFF000000) ;
   }
   
   // This will return the longest possible string at that location
   public String stringAt(int location)
   {
       String ret = "";
       
       for(int i = location; buffer[i] != 0 && i < currentLength; i++)
       {
           ret = ret + (char)buffer[i];
       }
       
       return ret;
   }
   
   // These function will remove stuff from the beginning of the packet
   // The overhead is a little nasty because everything has to be shifted
   // left first, but it's not so bad :)
   
   // First, we need a function that will erase characters from the beginning of a packet
   protected void shiftLeft(int amount)
   {
       for(int i = amount; i < currentLength; i++)
       {
           buffer[i - amount] = buffer[i];
       }
       
       currentLength -= amount;
   }
   
   public byte removeByte()
   {
       byte retVal = byteAt(0);

       shiftLeft(1);
       return retVal;
   }
   
   public short removeWord()
   {
       short retVal = wordAt(0);
       
       shiftLeft(2);
       return retVal;
   }
   
   public int removeDWord()
   {
       int retVal = dwordAt(0);
       
       shiftLeft(4);
       return retVal;
   }
   
   public String removeNTString()
   {
       StringBuffer str = new StringBuffer(buffer.length);
       
       while(buffer.length > 0 &&  buffer[0] != (byte) 0x00)
       {
           str.append(removeByte());
       }
       
       removeByte();
       
       return str.toString();
   }
   
   public String removeLine()
   {
       StringBuffer str = new StringBuffer(buffer.length);
       
       while(buffer.length > 0 &&  buffer[0] != (byte) '\n')
       {
           str.append(removeByte());
       }
       
       removeByte();
       
       return str.toString();
   }
   
       
       
       
   
   public final byte[] getBytes()
   {
       return buffer;
   }
   
   public String toString()
   {
       try
       {
           return new String(buffer, 0, length(), "US-ASCII");
       }
       catch(UnsupportedEncodingException e)
       {
           System.out.println("Error using charset US-ASCII!");
           System.out.println(e);

           return null;
       }
   }    
}
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Kp

You might want to use unsigned rightshift instead of normal rightshift (>>> instead of >>) in your addX functions that require shifting.  That should let you get away from needing a long to store a 32bit value.  Also, I haven't checked, but the compiler might get stupid and sign extend your 0xff000000, unless you write an L after it.  This would mean that the upper 32 bits of the long don't get cleared.  Your cast to byte ought to get around this, but it's still something to worry about. :)

Also, in verifyLength, you might achieve marginally better performance using System.arraycopy(...) instead of your own for loop (it might be able to switch to doing copies on a better granularity than byte, since it might be able to step outside of Java's type checking and use pointers after validating all inputs).

You could probably minimize overhead of shiftLeft (at the price of more memory consumed) by having an offset variable instead.  It would indicate how many bytes of "junk" data need to be skipped before reaching real data; then shiftLeft can just add to that, and the other functions take heed of it when seeking into the buffer.  If you did such a thing, you could also modify verifyLength to take advantage of the reallocation not to copy the leading junk data (and thereby reset offset to zero).
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

iago

#2
hmm, those are all good ideas, I think.. I'll play around with that later :)

Originally I had thought of doing the shiftLeft with an offset, but I thought it would be too much work.  But you're right, it would actually be a fairly good improvement on it
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Banana fanna fo fanna

java.nio.ByteBuffer

iago

Quote from: St0rm.iD on January 03, 2004, 11:02 PM
java.nio.ByteBuffer

Never heard of it.  And this works fine :P
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Banana fanna fo fanna

Or could use ByteBuffer, which is already written, high-performance, and has everything except insertString()