Author | Message | Time |
---|---|---|
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 |