Valhalla Legends Forums Archive | Java Programming | Buffer

AuthorMessageTime
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):

[code]// 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;
}
}
}
[/code]
January 1, 2004, 12:15 PM
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).
January 1, 2004, 4:58 PM
iago
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
January 2, 2004, 12:56 AM
St0rm.iD
java.nio.ByteBuffer
January 4, 2004, 5:02 AM
iago
[quote author=St0rm.iD link=board=34;threadid=4547;start=0#msg38275 date=1073192532]
java.nio.ByteBuffer
[/quote]

Never heard of it. And this works fine :P
January 4, 2004, 8:02 AM
St0rm.iD
Or could use ByteBuffer, which is already written, high-performance, and has everything except insertString()
January 5, 2004, 12:33 AM

Search