net.kano.joscar
Class ByteBlock

java.lang.Object
  extended bynet.kano.joscar.ByteBlock
All Implemented Interfaces:
LiveWritable, java.io.Serializable, Writable

public final class ByteBlock
extends java.lang.Object
implements Writable, java.io.Serializable

Provides a read-only interface to an underlying block of data. This class is useful for conserving memory (and CPU time copying memory) when many objects need to access different parts of a byte array.
For example, when a FLAP packet is read from a stream, a ByteBlock is created containing the FLAP data (bytes 6 through n-6-1, where n is the FLAP data length). If this is a channel-2 FLAP, then a SnacPacket is created, which creates a ByteBlock representing the SNAC data, or bytes 10 through n-10-1 of the FLAP data. Then, if this command is an ICBM (like an IM or chat message), an AbstractIcbmCommand subclass creates another ByteBlock to represent the "channel-specific data," and in most cases the channel data are read as a TlvChain, which splits that ByteBlock up into, in the case of IM, up to five separate TLV's, each containing a ByteBlock that represents some range of data within that TlvChain.

Now obviously it would be inefficient to be copying the majority of each IM packet's data at least four times. On the other hand, it would be tedious to pass an offset into the array and a length to every single method that needed some part of every packet that came in. Thus, ByteBlock was created, providing the convenience of a single object to represent data with the efficiency of accessing a single underlying byte[], and with the added security of being immutable (that is, read-only).

See Also:
Serialized Form

Field Summary
static ByteBlock EMPTY_BLOCK
          A ByteBlock with a length of 0.
 
Method Summary
 void copyTo(byte[] dest, int destOffset)
          Copies the data in this block to the given array at the given position.
static ByteBlock createByteBlock(LiveWritable writable)
          Creates a ByteBlock by writing the given LiveWritable to a byte array, then wrapping that array in a ByteBlock.
static ByteBlock createByteBlock(LiveWritable[] writables)
          Creates a ByteBlock by concatenating the output of the given list of LiveWritables.
static java.io.InputStream createInputStream(ByteBlock data)
          Returns an InputStream that simply reads from the given byte block.
static java.lang.String createString(ByteBlock block, java.lang.String charset)
          Creates a String from the data in the given block, using the given charset for encoding.
 boolean equals(java.lang.Object o)
          Returns true if this and the given object represent the same data, byte for byte; false otherwise.
 byte get(int index)
          Returns the byte in this block at the given index.
 int getLength()
          Returns the length of this byte block.
 int getOffset()
          Returns the offset into the backing array that represents the first index of this block.
 long getWritableLength()
          Provided in order to satisfy the requirements of Writable; returns the length of this byte block.
 int hashCode()
           
 ByteBlock subBlock(int offset)
          Returns a new ByteBlock containing all bytes in this block from offset to the end of this block.
 ByteBlock subBlock(int offset, int len)
          Returns a new ByteBlock containing the first len bytes in this block starting at index offset.
 byte[] toByteArray()
          Allocates a new byte array containing a copy of the contents of this byte block.
 java.lang.String toString()
           
static ByteBlock wrap(byte[] bytes)
          Returns a ByteBlock logically equivalent to the given byte array.
static ByteBlock wrap(byte[] bytes, int offset)
          Creates a ByteBlock that is simply a wrapper around the data in the given array after the given index.
static ByteBlock wrap(byte[] bytes, int offset, int len)
          Creates a ByteBlock that is simply a wrapper around the specified length of data in the given array after the given index.
 void write(java.io.OutputStream stream)
          Writes the contents of this ByteBlock to the given stream.
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Field Detail

EMPTY_BLOCK

public static final ByteBlock EMPTY_BLOCK
A ByteBlock with a length of 0.

Method Detail

wrap

public static ByteBlock wrap(byte[] bytes)
                      throws java.lang.IllegalArgumentException
Returns a ByteBlock logically equivalent to the given byte array. Calling this method is equivalent to calling ByteBlock.wrap(bytes, 0, bytes.length).

Note that while the ByteBlock class is described as "immutable" because there are no public methods that can modify the object's state, there is still the possibility that anyone with a reference to the given byte[] will still be able to modify its contents. Thus, ByteBlock is, in a sense, only as immutable as you make it.

Parameters:
bytes - the data to "wrap" in the ByteBlock
Returns:
a ByteBlock backed by the given array
Throws:
java.lang.IllegalArgumentException - if the given byte array is null
See Also:
wrap(byte[], int, int)

wrap

public static ByteBlock wrap(byte[] bytes,
                             int offset)
                      throws java.lang.IllegalArgumentException,
                             java.lang.IndexOutOfBoundsException
Creates a ByteBlock that is simply a wrapper around the data in the given array after the given index. Thus, ByteBlock.wrap(bytes, 50).get(0) == bytes[50], and ByteBlock.wrap(bytes, 50).getLength() == (bytes.length - 50).

Calling this method is equivalent to calling ByteBlock.wrap(bytes, offset, bytes.length - offset).

Note that while the ByteBlock class is described as "immutable" because there are no public methods that can modify the object's state, there is still the possibility that anyone with a reference to the given byte[] will still be able to modify its contents. Thus, ByteBlock is, in a sense, only as immutable as you make it.

Parameters:
bytes - the data that the returned ByteBlock will contain
offset - the starting index of the data to be held in the returned ByteBlock
Returns:
a ByteBlock backed by the given array after the given index
Throws:
java.lang.IllegalArgumentException - if the given array is null
java.lang.IndexOutOfBoundsException - if the given offset is less than zero or greater than the given array's length
See Also:
wrap(byte[], int, int)

wrap

public static ByteBlock wrap(byte[] bytes,
                             int offset,
                             int len)
                      throws java.lang.IllegalArgumentException,
                             java.lang.IndexOutOfBoundsException
Creates a ByteBlock that is simply a wrapper around the specified length of data in the given array after the given index. Thus, ByteBlock.wrap(bytes, 50, 3).get(0) == bytes[50], and ByteBlock.wrap(bytes, 50, 3).getLength() == 3.

Note that while the ByteBlock class is described as "immutable" because there are no public methods that can modify the object's state, there is still the possibility that anyone with a reference to the given byte[] will still be able to modify its contents. Thus, ByteBlock is, in a sense, only as immutable as you make it.

Parameters:
bytes - the data that the returned ByteBlock will contain
offset - the starting index of the data to be held in the returned ByteBlock
len - the number of bytes after index to hold in the returned ByteBlock
Returns:
a ByteBlock backed by the given number of bytes after the given index
Throws:
java.lang.IllegalArgumentException - if the given byte array is null
java.lang.IndexOutOfBoundsException - if the given offset is less than zero or greater than the length of the given byte array, or if the given length is less than zero or greater than the length of the given array minus the given offset (that is, if offset + len > bytes.length)

createByteBlock

public static ByteBlock createByteBlock(LiveWritable writable)
                                 throws java.lang.ArrayIndexOutOfBoundsException
Creates a ByteBlock by writing the given LiveWritable to a byte array, then wrapping that array in a ByteBlock.

Note that if writable is already a ByteBlock, no data will be copied but instead a block backed by the same array and with the same start index and data length will be created and returned.

Parameters:
writable - the object to wrap in a ByteBlock
Returns:
a ByteBlock containing the data written by writable.write([OutputStream])
Throws:
java.lang.ArrayIndexOutOfBoundsException - if writable.getWritableLength returns a number larger than can be stored in an array (n >= Integer.MAX_VALUE)

createByteBlock

public static ByteBlock createByteBlock(LiveWritable[] writables)
                                 throws java.lang.ArrayIndexOutOfBoundsException
Creates a ByteBlock by concatenating the output of the given list of LiveWritables.

Parameters:
writables - the list of LiveWritables to concatenate
Returns:
a ByteBlock containing the data output from each of the given LiveWritables, in the given order
Throws:
java.lang.ArrayIndexOutOfBoundsException - if the total size is greater than the maximum size of an array (n >= Integer.MAX_VALUE)

createString

public static java.lang.String createString(ByteBlock block,
                                            java.lang.String charset)
                                     throws java.io.UnsupportedEncodingException
Creates a String from the data in the given block, using the given charset for encoding. This is implemented for performance reasons so that the data need not be copied to a second array to be converted to a String.

Parameters:
block - the block of data to convert to a String
charset - the charset with which to decode the given data
Returns:
a String decoded with the given charset from the given block of data
Throws:
java.io.UnsupportedEncodingException - if the given encoding is not supported by the VM
See Also:
String.String(byte[], int, int, String)

createInputStream

public static java.io.InputStream createInputStream(ByteBlock data)
Returns an InputStream that simply reads from the given byte block. Semantics of the returned InputStream are those of ByteArrayOutputStream.

Parameters:
data - the block for which a stream should be created
Returns:
an input stream using the given byte block as a backing buffer

get

public final byte get(int index)
               throws java.lang.IndexOutOfBoundsException
Returns the byte in this block at the given index.

Parameters:
index - the index of the byte to return
Returns:
the byte at the given index of this block
Throws:
java.lang.IndexOutOfBoundsException - if the given index is less than zero or greater than this block's length (getLength())

getLength

public int getLength()
Returns the length of this byte block.

Returns:
the length of this block

getWritableLength

public long getWritableLength()
Provided in order to satisfy the requirements of Writable; returns the length of this byte block.

Specified by:
getWritableLength in interface Writable
Returns:
the length of this byte block

write

public void write(java.io.OutputStream stream)
           throws java.io.IOException
Writes the contents of this ByteBlock to the given stream.

Specified by:
write in interface Writable
Parameters:
stream - the stream to which to write this block
Throws:
java.io.IOException - if an I/O error occurs

toByteArray

public byte[] toByteArray()
Allocates a new byte array containing a copy of the contents of this byte block.

Returns:
a newly allocated byte array containing a copy of this byte block

subBlock

public ByteBlock subBlock(int offset)
                   throws java.lang.IndexOutOfBoundsException
Returns a new ByteBlock containing all bytes in this block from offset to the end of this block. Calling this method is the equivalent of calling block.subBlock(offset.getLength() - offset).

The new block will exist such that block.subBlock(5).get(0) == block.get(5), and block.subBlock(5).getLength() == (block.getLength() - 5).

Note that this method does not modify this block, but merely creates a new one with the same backing array and a new offset and length.

Parameters:
offset - the first index of the data to hold in the new ByteBlock
Returns:
a new ByteBlock holding all of the bytes in this block starting at the given offset
Throws:
java.lang.IndexOutOfBoundsException

subBlock

public ByteBlock subBlock(int offset,
                          int len)
                   throws java.lang.IndexOutOfBoundsException
Returns a new ByteBlock containing the first len bytes in this block starting at index offset.

The new block will exist such that block.subBlock(5, 2).get(0) == block.get(5), and block.subBlock(5, 2).getLength() == 2.

Note that no copying of data is done during this process, so feel free to use this method freely in a free manner.

Parameters:
offset - the first index of the data to hold in the new ByteBlock
len - the number of bytes that the new ByteBlock shall hold
Returns:
a new ByteBlock containing the specified number of bytes in this block starting at the specified index
Throws:
java.lang.IndexOutOfBoundsException - if the specified offset or length is negative or if offset + length > getLength()

getOffset

public int getOffset()
Returns the offset into the backing array that represents the first index of this block. Useful only for comparing two blocks that use the same backing array. For example, if b = a.subBlock(50, 2).subBlock(20), then b.getOffset() - a.getOffset() == 70.

Returns:
the offset into the backing array that represents the first index of this block

copyTo

public void copyTo(byte[] dest,
                   int destOffset)
Copies the data in this block to the given array at the given position.

Parameters:
dest - the array to which to copy this block
destOffset - the offset into the given array at which to begin copying this block

equals

public final boolean equals(java.lang.Object o)
Returns true if this and the given object represent the same data, byte for byte; false otherwise.

Parameters:
o - another ByteBlock to compare this block to
Returns:
true if this block represents the same data as the given block

hashCode

public final int hashCode()

toString

public java.lang.String toString()