/*******************************************************************************
An implementation of the Java Debug Wire Protocol (JDWP) for JOP
Copyright (C) 2007 Paulo Abadie Guedes
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*******************************************************************************/
package debug.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import debug.JopDebugKernel;
/**
* RandomAccessByteArrayOutputStream.java
*
* An output stream which provide more control over its
* internal byte array. Useful to create data blocks when
* the initial fields are not known in advance.
*
* Can be reused, to avoid much garbage collection.
* It provide also some methods to read its content.
* Yes, that may seem very odd. But now it looks like
* it's also a quick and reasonably good choice.
*
* @author Paulo Abadie Guedes
*
* 06/12/2007 - 10:24:09
*
*/
public class RandomAccessByteArrayOutputStream extends ByteArrayOutputStream
{
/**
* The default constructor of this class.
*/
public RandomAccessByteArrayOutputStream()
{
super();
}
/**
* Seek the cursor to the given location, by changing the internal
* pointer. This can be used to overwrite initial locations.
*
* @param location
*/
private synchronized void seek(int location)
{
if(location >= 0 && location < buf.length)
{
count = location;
}
}
/**
* write a short value to the stream.
*
* @param value
*/
public synchronized void writeShort(int value)
{
write ((byte) (0xff & (value >> 8)));
write ((byte) (0xff & value));
}
/**
* write an int value to the stream.
*
* @param value
*/
public synchronized void writeInt(int value)
{
write ((byte) (0xff & (value >> 24)));
write ((byte) (0xff & (value >> 16)));
write ((byte) (0xff & (value >> 8)));
write ((byte) (0xff & value));
}
/**
* Read one int value from a given location.
* This method provide read access to the packet content.
*
* Although it may seem really strange to provide read
* access to an output stream, this seems now to
* be the easiest way out to manipulate the packet
* content without causing it to allocate memory.
*
* @param location
* @return
*/
public synchronized int readInt(int location)
{
return readBytes(location, 4);
}
/**
* Read one short value from a given location.
*
* @param location
* @return
*/
public synchronized int readShort(int location)
{
return readBytes(location, 4);
}
/**
* Read one byte value from a given location.
*
* @param location
* @return
*/
public synchronized int readByte(int location)
{
return readBytes(location, 1);
}
/**
* Read a set of bytes from the stream. Limited from
* 1 until 4 bytes.
*
* @param location
* @param size
* @return
*/
private synchronized int readBytes(int location, int size)
{
// JopDebugKernel.debugPrint("Read bytes - Location:");
// JopDebugKernel.debugPrint(location);
// JopDebugKernel.debugPrint(" Size:");
// JopDebugKernel.debugPrint(size);
int value;
int i;
if((size < 1) || (size > 4) || (location + size > buf.length))
{
throw new ArrayIndexOutOfBoundsException("Wrong location(" + location +
") or size(" + size + ")!");
}
value = 0;
for(i = 0; i < size; i++)
{
// the first shift does nothing, but the others do.
value <<= 8;
// be careful! need to cut the first byte ONLY because the machine
// work with int values (see the next "or").
value = value | (buf[location] & 0x00ff);
location++;
}
// JopDebugKernel.debugPrint(" value:");
// JopDebugKernel.debugPrintln(value);
return value;
}
/**
* Overwrite an int value on the given location.
*
* If the location is outside the
* internal buffer range, ignore the request and return.
*
* @param value
* @param location
*/
public synchronized void overwriteInt(int value, int location)
{
overwriteData(value, location, 4);
}
/**
* Overwrite a short value on the given location.
*
* If the location is outside the
* internal buffer range, ignore the request and return.
*
* @param value
* @param location
*/
public synchronized void overwriteShort(int value, int location)
{
overwriteData(value, location, 2);
}
/**
* Overwrite a byte value on the given location.
*
* If the location is outside the
* internal buffer range, ignore the request and return.
*
* @param value
* @param location
*/
public synchronized void overwriteByte(int value, int location)
{
overwriteData(value, location, 1);
}
/**
* Overwrite a set of bytes on the internal array.
* Used to overwrite int, byte, short and long.
*
* If the location is outside the internal
* buffer range, ignore the request and return.
*
* @param value
* @param location
* @param size
*/
private void overwriteData(int value, int location, int size)
{int dif;
int currentLocation;
// get the current location, to be restored later.
currentLocation = size();
// if there's not enough room, ignore it and return.
dif = (buf.length - size) - location;
if(dif < 0)
{
return;
}
// seek to the position to be overwritten.
seek(location);
// write the value
switch(size)
{
case 4:
{
writeInt(value);
break;
}
case 2:
{
writeShort(value);
break;
}
case 1:
{
write(value);
break;
}
default:
{
// do nothing. This should *not* happen, as the method is
// a private one. Hence, used only internally.
break;
}
}
// restore the pointer to the previous location.
seek(currentLocation);
}
/**
* This method write the internal content to an output stream.
* It's used to avoid allocating and releasing new objects for every
* packet sent.
*
* @param outputStream
* @throws IOException
*/
public synchronized void writeContent(OutputStream outputStream) throws IOException
{
int size = size();
// don't write the entire buffer. Instead, write just what's necessary.
outputStream.write(buf, 0, size);
}
}