/*
* Player Java Client 3 - XdrBufferDecodingStream.java
* Copyright (C) 2002-2006 Radu Bogdan Rusu, Maxim Batalin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*
*/
/*
* Copyright (c) 1999, 2000
* Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
* D-52064 Aachen, Germany.
* All rights reserved.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package javaclient3.xdr;
import java.io.IOException;
import java.net.InetAddress;
/**
* The <code>XdrBufferDecodingStream</code> class provides the necessary
* functionality to {@link XdrDecodingStream} to retrieve XDR packets from
* a byte buffer.
*
* @version $Revision$ $Date$ $State$ $Locker$
* @author Harald Albrecht
*/
public class XdrBufferDecodingStream extends XdrDecodingStream {
/**
* Construct a new <code>XdrUdpDecodingStream</code> object and associate
* it with a buffer containing encoded XDR data.
*
* @param buffer Buffer containing encoded XDR data.
* @param encodedLength Length of encoded XDR data within the buffer.
*
* @throws IllegalArgumentException if <code>encodedLength</code> is not
* a multiple of four.
*/
public XdrBufferDecodingStream(byte [] buffer, int encodedLength) {
setXdrData(buffer, encodedLength);
}
/**
* Construct a new <code>XdrUdpDecodingStream</code> object and associate
* it with a buffer containing encoded XDR data.
*
* @param buffer Buffer containing encoded XDR data.
*
* @throws IllegalArgumentException if the size of the buffer is not
* a multiple of four.
*/
public XdrBufferDecodingStream(byte [] buffer) {
setXdrData(buffer, buffer.length);
}
/**
* Sets the buffer containing encoded XDR data as well as the length of
* the encoded data.
*
* @param buffer Buffer containing encoded XDR data.
* @param encodedLength Length of encoded XDR data within the buffer.
*
* @throws IllegalArgumentException if <code>encodedLength</code> is not
* a multiple of four.
*/
public void setXdrData(byte [] buffer, int encodedLength) {
//
// Make sure that the buffer size is a multiple of four, otherwise
// throw an exception.
//
if ( (encodedLength < 0)
|| (encodedLength & 3) != 0 ) {
throw(new IllegalArgumentException("length of encoded data must be a multiple of four and must not be negative"));
}
this.buffer = buffer;
this.encodedLength = encodedLength;
bufferIndex = 0;
bufferHighmark = -4;
}
/**
* Returns the Internet address of the sender of the current XDR data.
* This method should only be called after {@link #beginDecoding},
* otherwise it might return stale information.
*
* @return InetAddress of the sender of the current XDR data.
*/
public InetAddress getSenderAddress() {
return null;
}
/**
* Returns the port number of the sender of the current XDR data.
* This method should only be called after {@link #beginDecoding},
* otherwise it might return stale information.
*
* @return Port number of the sender of the current XDR data.
*/
public int getSenderPort() {
return 0;
}
/**
* Initiates decoding of the next XDR record.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public void beginDecoding()
throws OncRpcException, IOException {
bufferIndex = 0;
bufferHighmark = encodedLength - 4;
}
/**
* End decoding of the current XDR record. The general contract of
* <code>endDecoding</code> is that calling it is an indication that
* the current record is no more interesting to the caller and any
* allocated data for this record can be freed.
*
* <p>This method overrides {@link XdrDecodingStream#endDecoding}. It does nothing
* more than resetting the buffer pointer (eeek! a pointer in Java!!!) back
* to the begin of an empty buffer, so attempts to decode data will fail
* until the buffer is filled again.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public void endDecoding()
throws OncRpcException, IOException {
bufferIndex = 0;
bufferHighmark = -4;
}
/**
* Closes this decoding XDR stream and releases any system resources
* associated with this stream. A closed XDR stream cannot perform decoding
* operations and cannot be reopened.
*
* <p>This implementation frees the allocated buffer.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public void close()
throws OncRpcException, IOException {
buffer = null;
}
/**
* Decodes (aka "deserializes") a "XDR int" value received from a
* XDR stream. A XDR int is 32 bits wide -- the same width Java's "int"
* data type has.
*
* @return The decoded int value.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public int xdrDecodeInt()
throws OncRpcException, IOException {
if ( bufferIndex <= bufferHighmark ) {
//
// There's enough space in the buffer to hold at least one
// XDR int. So let's retrieve it now.
// Note: buffer[...] gives a byte, which is signed. So if we
// add it to the value (which is int), it has to be widened
// to 32 bit, so its sign is propagated. To avoid this sign
// madness, we have to "and" it with 0xFF, so all unwanted
// bits are cut off after sign extension. Sigh.
//
int value = buffer[bufferIndex++];
value = (value << 8) + (buffer[bufferIndex++] & 0xFF);
value = (value << 8) + (buffer[bufferIndex++] & 0xFF);
value = (value << 8) + (buffer[bufferIndex++] & 0xFF);
return value;
} else {
throw(new OncRpcException(OncRpcException.RPC_BUFFERUNDERFLOW));
}
}
/**
* Decodes (aka "deserializes") an opaque value, which is nothing more
* than a series of octets (or 8 bits wide bytes). Because the length
* of the opaque value is given, we don't need to retrieve it from the
* XDR stream. This is different from
* {@link #xdrDecodeOpaque(byte[], int, int)} where
* first the length of the opaque value is retrieved from the XDR stream.
*
* @param length Length of opaque data to decode.
*
* @return Opaque data as a byte vector.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public byte [] xdrDecodeOpaque(int length)
throws OncRpcException, IOException {
//
// First make sure that the length is always a multiple of four.
//
int alignedLength = length;
if ( (alignedLength & 3) != 0 ) {
alignedLength = (alignedLength & ~3) + 4;
}
//
// Now allocate enough memory to hold the data to be retrieved.
//
byte [] bytes = new byte[length];
if ( length > 0 ) {
if ( bufferIndex <= bufferHighmark - alignedLength + 4 ) {
System.arraycopy(buffer, bufferIndex, bytes, 0, length);
} else {
throw(new OncRpcException(OncRpcException.RPC_BUFFERUNDERFLOW));
}
}
bufferIndex += alignedLength;
return bytes;
}
/**
* Decodes (aka "deserializes") a XDR opaque value, which is represented
* by a vector of byte values, and starts at <code>offset</code> with a
* length of <code>length</code>. Only the opaque value is decoded, so the
* caller has to know how long the opaque value will be. The decoded data
* is always padded to be a multiple of four (because that's what the
* sender does).
*
* @param opaque Byte vector which will receive the decoded opaque value.
* @param offset Start offset in the byte vector.
* @param length the number of bytes to decode.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public void xdrDecodeOpaque(byte [] opaque, int offset, int length)
throws OncRpcException, IOException {
//
// First make sure that the length is always a multiple of four.
//
int alignedLength = length;
if ( (alignedLength & 3) != 0 ) {
alignedLength = (alignedLength & ~3) + 4;
}
//
// Now allocate enough memory to hold the data to be retrieved.
//
if ( length > 0 ) {
if ( bufferIndex <= bufferHighmark - alignedLength + 4 ) {
System.arraycopy(buffer, bufferIndex, opaque, offset, length);
} else {
throw(new OncRpcException(OncRpcException.RPC_BUFFERUNDERFLOW));
}
}
bufferIndex += alignedLength;
}
/**
* The buffer which will be filled from the datagram socket and then
* be used to supply the information when decoding data.
*/
private byte [] buffer;
/**
* Length of encoded data in <code>buffer</code>.
*/
private int encodedLength;
/**
* The read pointer is an index into the <code>buffer</code>.
*/
private int bufferIndex;
/**
* Index of the last four byte word in the buffer, which has been read
* in from the datagram socket.
*/
private int bufferHighmark;
}
// End of XdrBufferDecodingStream.java