Util.Buffer
package util;
/** Buffer.java
* This class makes it easy for the programmer to create an array of bytes * of a specific format. It'll support these major types:
* DWord - 4 bytes
* Word - 2 bytes
* Byte - 1 byte
* NTString - Any number of bytes, interpreted as characters, terminated by a null.
*
* This is a pretty standard buffer in terms of everything else. I've used it extensively
* and have complete confidence in it.
*
* @author Ron
*/
public class Buffer
{
/** The starting length of the buffer */
public final int defaultLength = 32;
/** The actual buffer which will hold the data */
protected byte[] buffer;
/** The current length of the buffer */
private int currentLength;
/** The maximum length of the buffer */
private int maxLength;
/** Initializes the variables */
public Buffer()
{
buffer = new byte[defaultLength];
currentLength = 0;
maxLength = defaultLength;
}
/** Initialize the buffer to an array of bytes.
* @param b The initial value.
*/
public Buffer(byte[] b)
{
this();
addBytes(b);
}
/** Initializes the buffer to another buffer. Note that it makes a copy, it doesn't share
* the data.
* @param b The original buffer.
*/
public Buffer(Buffer b)
{
this();
addBytes(b.getBytes());
}
public Buffer(char []c)
{
this();
for(int i = 0; i < c.length; i++)
addByte((byte) c[i]);
}
/** Returns the size of the buffer */
public int size()
{
return currentLength;
}
/** Returns the entire buffer as an array of bytes.
* @return The entire buffer as an array of bytes.
*/
public byte[] getBytes()
{
byte[] ret = new byte[currentLength];
System.arraycopy(buffer, 0, ret, 0, currentLength);
return ret;
}
/** Ensures that there is enough length to store the specified number of bytes. If there
* isn't, enough extra room is allocated so we can.
*
* @param bytes The number of bytes we'return adding to it.
*/
private void verifyLength(int bytes)
{
// If we already have enough, just return
if((currentLength + bytes) <= maxLength)
return;
while((currentLength + bytes) > maxLength)
{
maxLength = maxLength * 2;
}
// Create a new buffer
byte[] newBuffer = new byte[maxLength];
// Copy the old buffer into the new buffer
System.arraycopy(buffer, 0, newBuffer, 0, currentLength);
// Set the current buffer to the new buffer
buffer = newBuffer;
}
/** Shifts all data back by the requested number of bytes, and returns the
* bytes that were shifted off.
*
* @param number The number of bytes to pull off the beginning.
* @return The bytes that were pulled off.
*/
protected byte[] remove(int number)
{
byte[] ret = new byte[number];
System.arraycopy(buffer, 0, ret, 0, number);
System.arraycopy(buffer, number, buffer, 0, currentLength - number);
currentLength -= number;
return ret;
}
/** Adds a single byte to the end of the buffer .
* @param b The byte to add.
*/
public void addByte(byte b)
{
verifyLength(1);
buffer[currentLength++] = b;
}
/** Removes a single byte from the beginning of the buffer.
* @return The byte that was removed.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public byte removeByte() throws IndexOutOfBoundsException
{
if(currentLength == 0)
throw new IndexOutOfBoundsException("Attempted to remove data from the buffer that wasn't there.");
return remove(1)[0];
}
/** Returns the byte at a specific location.
* @param index The location to get the byte at.
* @return The byte at location "index".
*/
public byte byteAt(int index)
{
return buffer[index];
}
/** Adds a word to the buffer (2 bytes, little endian).
* @param w The word to add.
*/
public void addWord(short w)
{
addByte((byte)((w & 0x00FF) >> 0));
addByte((byte)((w & 0xFF00) >> 8));
}
/** Removes and returns a single word (2 bytes).
* @return The word that was removed.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public short removeWord() throws IndexOutOfBoundsException
{
int ret = ((removeByte() << 0) & 0x000000FF) |
((removeByte() << 8) & 0x0000FF00);
return (short)(ret & 0x0000FFFF);
}
/** Adds a dword to the buffer (4 bytes, little endian).
* @param d The dword to add.
*/
public void addDWord(int d)
{
addByte((byte)((d & 0x000000FF) >> 0));
addByte((byte)((d & 0x0000FF00) >> 8));
addByte((byte)((d & 0x00FF0000) >> 16));
addByte((byte)((d & 0xFF000000) >> 24));
}
public void addArray(int []a)
{
for(int i = 0; i < a.length; i++)
addDWord(a[i]);
}
/** Removes and returns a single dword (4 bytes).
* @return The DWord that was removed.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public int removeDWord() throws IndexOutOfBoundsException
{
return ((removeByte() << 0) & 0x000000FF) |
((removeByte() << 8) & 0x0000FF00) |
((removeByte() << 16) & 0x00FF0000) |
((removeByte() << 24) & 0xFF000000);
}
/** Adds a QWord to the buffer (8 bytes, little endian)
* @param l The value to add.
*/
public void addLong(long l)
{
addByte((byte)((l & 0x00000000000000FFl) >> 0l));
addByte((byte)((l & 0x000000000000FF00l) >> 8l));
addByte((byte)((l & 0x0000000000FF0000l) >> 16l));
addByte((byte)((l & 0x00000000FF000000l) >> 24l));
addByte((byte)((l & 0x000000FF00000000l) >> 32l));
addByte((byte)((l & 0x0000FF0000000000l) >> 40l));
addByte((byte)((l & 0x00FF000000000000l) >> 48l));
addByte((byte)((l & 0xFF00000000000000l) >> 56l));
}
/** Removes and returns a single dword (8 bytes).
* @return The long at the beginning of the buffer.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public long removeLong() throws IndexOutOfBoundsException
{
return (((long)removeByte() << 0L) & 0x00000000000000FFL) |
(((long)removeByte() << 8L) & 0x000000000000FF00L) |
(((long)removeByte() << 16L) & 0x0000000000FF0000L) |
(((long)removeByte() << 24L) & 0x00000000FF000000L) |
(((long)removeByte() << 32L) & 0x000000FF00000000L) |
(((long)removeByte() << 40L) & 0x0000FF0000000000L) |
(((long)removeByte() << 48L) & 0x00FF000000000000L) |
(((long)removeByte() << 56L) & 0xFF00000000000000L);
}
/** Adds a non-null-terminated string to the buffer
* @param s The string to add to the buffer.
*/
public void addString(String s)
{
for(int i = 0; i < s.length(); i++)
{
addByte((byte) s.charAt(i));
}
}
/** Removes 'i' bytes from the buffer, and returns them as a string
* @param i The number of bytes to remove.
* @return The bytes removed as a String.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public String removeString(int i) throws IndexOutOfBoundsException
{
StringBuffer s = new StringBuffer(i + 1);
for(int j = 0; j < i; j++)
{
s.append((char) removeByte());
}
return s.toString();
}
/** Adds a null-terminated string to the buffer, including the null
* @param s The string to add, without the null terminator.
*/
public void addNTString(String s)
{
addString(s);
addByte((byte) 0x00);
}
/** Removes and returns a null-terminated string, without the null
* @return The first null-terminatred string in the buffer, without the null.
* @throws IndexOutOfBoundsException If the buffer ended before finding a Null.
*/
public String removeNTString()
{
StringBuffer s = new StringBuffer();
byte b = removeByte();
while(b != (byte) 0x00)
{
s.append((char) (b & 0x000000FF));
b = removeByte();
}
return s.toString();
}
/** Adds an array of bytes to the buffer
* @param b The bytes to add to the buffer.
*/
public void addBytes(byte[] b)
{
for(int i = 0; i < b.length; i++)
addByte(b[i]);
}
/** Removes 'i' bytes from the array and returns them as an array of bytes.
* @param i The number of bytes to remove.
* @return The bytes that were removed.
* @throws IndexOutOfBoundsException If there isn't enough room in the buffer to accomidate the
* requested removal.
*/
public byte[] removeBytes(int i) throws IndexOutOfBoundsException
{
return remove(i);
}
/** Adds another buffer to the end of the buffer. All it actually does is add the bytes
* of the source to the current buffer.
* @param b The buffer to add.
*/
public void addBuffer(Buffer b)
{
addBytes(b.getBytes());
}
/** Quicly add a byte to the buffer.
* @param b The byte to add.
*/
public void add(byte b)
{
addByte(b);
}
/** Quickly add a short to the buffer.
* @param s The short to add.
*/
public void add(short s)
{
addWord(s);
}
/** Quickly add an int to the buffer.
* @param i The inteter to add.
*/
public void add(int i)
{
addDWord(i);
}
public void add(int []i)
{
addArray(i);
}
/** Quickly add a long to the buffer.
* @param l The long to add.
*/
public void add(long l)
{
addLong(l);
}
/** Quickly add an array of bytes to the buffer.
* @param b The array of bytes.
*/
public void add(byte[] b)
{
addBytes(b);
}
/** Quickly append another buffer to the current buffer.
* @param b The source buffer.
*/
public void add(Buffer b)
{
addBuffer(b);
}
/** Gets the buffer, formatted in a pretty way.
* @return The formatted string. It might look something like this:
*
* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................ * 69 68 67 66 65 64 63 62 61 61 6A 6B 6C 6D 6E 00 ihgfedcbaajklmn. * 41 00 A. * Length: 34 *
*/ public String toString() { 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(); }
}