
org.apache.mina.util.byteaccess.CompositeByteArray Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.mina.util.byteaccess;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.util.byteaccess.ByteArrayList.Node;
/**
* A ByteArray composed of other ByteArrays. Optimised for fast relative access
* via cursors. Absolute access methods are provided, but may perform poorly.
*
* TODO: Write about laziness of cursor implementation - how movement doesn't
* happen until actual get/put.
*
* @author Apache MINA Project
*/
public final class CompositeByteArray extends AbstractByteArray {
/**
* Allows for efficient detection of component boundaries when using a cursor.
*
* TODO: Is this interface right?
*/
public interface CursorListener {
/**
* Called when the first component in the composite is entered by the cursor.
*/
public void enteredFirstComponent( int componentIndex, ByteArray component );
/**
* Called when the next component in the composite is entered by the cursor.
*/
public void enteredNextComponent( int componentIndex, ByteArray component );
/**
* Called when the previous component in the composite is entered by the cursor.
*/
public void enteredPreviousComponent( int componentIndex, ByteArray component );
/**
* Called when the last component in the composite is entered by the cursor.
*/
public void enteredLastComponent( int componentIndex, ByteArray component );
}
/**
* Stores the underlying ByteArray
s.
*/
private final ByteArrayList bas = new ByteArrayList();
/**
* The byte order for data in the buffer
*/
private ByteOrder order;
/**
* May be used in getSingleIoBuffer
. Optional.
*/
private final ByteArrayFactory byteArrayFactory;
/**
* Creates a new instance of CompositeByteArray.
*/
public CompositeByteArray() {
this( null );
}
/**
*
* Creates a new instance of CompositeByteArray.
*
* @param byteArrayFactory
* The factory used to create the ByteArray objects
*/
public CompositeByteArray( ByteArrayFactory byteArrayFactory ) {
this.byteArrayFactory = byteArrayFactory;
}
/**
* Returns the first {@link ByteArray} in the list
*
* @return
* The first ByteArray in the list
*/
public ByteArray getFirst() {
if ( bas.isEmpty() ) {
return null;
}
return bas.getFirst().getByteArray();
}
/**
* Adds the specified {@link ByteArray} to the first
* position in the list
*
* @param ba
* The ByteArray to add to the list
*/
public void addFirst( ByteArray ba ) {
addHook( ba );
bas.addFirst( ba );
}
/**
* Remove the first {@link ByteArray} in the list
*
* @return
* The first ByteArray in the list
*/
public ByteArray removeFirst() {
Node node = bas.removeFirst();
return node == null ? null : node.getByteArray();
}
/**
* Remove component ByteArray
s to the given index (splitting
* them if necessary) and returning them in a single ByteArray
.
* The caller is responsible for freeing the returned object.
*
* TODO: Document free behaviour more thoroughly.
*/
public ByteArray removeTo( int index ) {
if ( index < first() || index > last() ) {
throw new IndexOutOfBoundsException();
}
// Optimisation when removing exactly one component.
// if (index == start() + getFirst().length()) {
// ByteArray component = getFirst();
// removeFirst();
// return component;
// }
// Removing
CompositeByteArray prefix = new CompositeByteArray( byteArrayFactory );
int remaining = index - first();
while ( remaining > 0 ) {
ByteArray component = removeFirst();
if ( component.last() <= remaining ) {
// Remove entire component.
prefix.addLast( component );
remaining -= component.last();
} else {
// Remove part of component. Do this by removing entire
// component then readding remaining bytes.
// TODO: Consider using getIoBuffers(), as would avoid
// performance problems for nested ComponentByteArrays.
IoBuffer bb = component.getSingleIoBuffer();
// get the limit of the buffer
int originalLimit = bb.limit();
// set the position to the beginning of the buffer
bb.position( 0 );
// set the limit of the buffer to what is remaining
bb.limit( remaining );
// create a new IoBuffer, sharing the data with 'bb'
IoBuffer bb1 = bb.slice();
// set the position at the end of the buffer
bb.position( remaining );
// gets the limit of the buffer
bb.limit( originalLimit );
// create a new IoBuffer, sharing teh data with 'bb'
IoBuffer bb2 = bb.slice();
// create a new ByteArray with 'bb1'
ByteArray ba1 = new BufferByteArray( bb1 ) {
@Override
public void free() {
// Do not free. This will get freed
}
};
// add the new ByteArray to the CompositeByteArray
prefix.addLast( ba1 );
remaining -= ba1.last();
// final for anonymous inner class
final ByteArray componentFinal = component;
ByteArray ba2 = new BufferByteArray( bb2 ) {
@Override
public void free() {
componentFinal.free();
}
};
// add the new ByteArray to the CompositeByteArray
addFirst( ba2 );
}
}
// return the CompositeByteArray
return prefix;
}
/**
* Adds the specified {@link ByteArray} to the end of the list
*
* @param ba
* The ByteArray to add to the end of the list
*/
public void addLast( ByteArray ba ) {
addHook( ba );
bas.addLast( ba );
}
/**
* Removes the last {@link ByteArray} in the list
*
* @return
* The ByteArray that was removed
*/
public ByteArray removeLast() {
Node node = bas.removeLast();
return node == null ? null : node.getByteArray();
}
/**
* @inheritDoc
*/
public void free() {
while ( !bas.isEmpty() ) {
Node node = bas.getLast();
node.getByteArray().free();
bas.removeLast();
}
}
private void checkBounds( int index, int accessSize ) {
int lower = index;
int upper = index + accessSize;
if ( lower < first() ) {
throw new IndexOutOfBoundsException( "Index " + lower + " less than start " + first() + "." );
}
if ( upper > last() ) {
throw new IndexOutOfBoundsException( "Index " + upper + " greater than length " + last() + "." );
}
}
/**
* @inheritDoc
*/
public Iterable getIoBuffers() {
if ( bas.isEmpty() ) {
return Collections.emptyList();
}
Collection result = new ArrayList();
Node node = bas.getFirst();
for ( IoBuffer bb : node.getByteArray().getIoBuffers() ) {
result.add( bb );
}
while ( node.hasNextNode() ) {
node = node.getNextNode();
for ( IoBuffer bb : node.getByteArray().getIoBuffers() ) {
result.add( bb );
}
}
return result;
}
/**
* @inheritDoc
*/
public IoBuffer getSingleIoBuffer() {
if ( byteArrayFactory == null ) {
throw new IllegalStateException(
"Can't get single buffer from CompositeByteArray unless it has a ByteArrayFactory." );
}
if ( bas.isEmpty() ) {
ByteArray ba = byteArrayFactory.create( 1 );
return ba.getSingleIoBuffer();
}
int actualLength = last() - first();
{
Node node = bas.getFirst();
ByteArray ba = node.getByteArray();
if ( ba.last() == actualLength ) {
return ba.getSingleIoBuffer();
}
}
// Replace all nodes with a single node.
ByteArray target = byteArrayFactory.create( actualLength );
IoBuffer bb = target.getSingleIoBuffer();
Cursor cursor = cursor();
cursor.put( bb ); // Copy all existing data into target IoBuffer.
while ( !bas.isEmpty() ) {
Node node = bas.getLast();
ByteArray component = node.getByteArray();
bas.removeLast();
component.free();
}
bas.addLast( target );
return bb;
}
/**
* @inheritDoc
*/
public Cursor cursor() {
return new CursorImpl();
}
/**
* @inheritDoc
*/
public Cursor cursor( int index ) {
return new CursorImpl( index );
}
/**
* Get a cursor starting at index 0 (which may not be the start of the
* array) and with the given listener.
*
* @param listener
* Returns a new {@link Cursor} instance
*/
public Cursor cursor( CursorListener listener ) {
return new CursorImpl( listener );
}
/**
* Get a cursor starting at the given index and with the given listener.
*
* @param index
* The position of the array to start the Cursor at
* @param listener
* The listener for the Cursor that is returned
*/
public Cursor cursor( int index, CursorListener listener ) {
return new CursorImpl( index, listener );
}
/**
* @inheritDoc
*/
public ByteArray slice( int index, int length ) {
return cursor( index ).slice( length );
}
/**
* @inheritDoc
*/
public byte get( int index ) {
return cursor( index ).get();
}
/**
* @inheritDoc
*/
public void put( int index, byte b )
{
cursor( index ).put( b );
}
/**
* @inheritDoc
*/
public void get( int index, IoBuffer bb )
{
cursor( index ).get( bb );
}
/**
* @inheritDoc
*/
public void put( int index, IoBuffer bb )
{
cursor( index ).put( bb );
}
/**
* @inheritDoc
*/
public int first()
{
return bas.firstByte();
}
/**
* @inheritDoc
*/
public int last()
{
return bas.lastByte();
}
/**
* This method should be called prior to adding any component
* ByteArray
to a composite.
*
* @param ba
* The component to add.
*/
private void addHook( ByteArray ba )
{
// Check first() is zero, otherwise cursor might not work.
// TODO: Remove this restriction?
if ( ba.first() != 0 )
{
throw new IllegalArgumentException( "Cannot add byte array that doesn't start from 0: " + ba.first() );
}
// Check order.
if ( order == null )
{
order = ba.order();
}
else if ( !order.equals( ba.order() ) )
{
throw new IllegalArgumentException( "Cannot add byte array with different byte order: " + ba.order() );
}
}
/**
* @inheritDoc
*/
public ByteOrder order()
{
if ( order == null )
{
throw new IllegalStateException( "Byte order not yet set." );
}
return order;
}
/**
* @inheritDoc
*/
public void order( ByteOrder order ) {
if ( order == null || !order.equals( this.order ) ) {
this.order = order;
if ( !bas.isEmpty() ) {
for ( Node node = bas.getFirst(); node.hasNextNode(); node = node.getNextNode() ) {
node.getByteArray().order( order );
}
}
}
}
/**
* @inheritDoc
*/
public short getShort( int index ) {
return cursor( index ).getShort();
}
/**
* @inheritDoc
*/
public void putShort( int index, short s ) {
cursor( index ).putShort( s );
}
/**
* @inheritDoc
*/
public int getInt( int index )
{
return cursor( index ).getInt();
}
/**
* @inheritDoc
*/
public void putInt( int index, int i )
{
cursor( index ).putInt( i );
}
/**
* @inheritDoc
*/
public long getLong( int index )
{
return cursor( index ).getLong();
}
/**
* @inheritDoc
*/
public void putLong( int index, long l )
{
cursor( index ).putLong( l );
}
/**
* @inheritDoc
*/
public float getFloat( int index )
{
return cursor( index ).getFloat();
}
/**
* @inheritDoc
*/
public void putFloat( int index, float f )
{
cursor( index ).putFloat( f );
}
/**
* @inheritDoc
*/
public double getDouble( int index )
{
return cursor( index ).getDouble();
}
/**
* @inheritDoc
*/
public void putDouble( int index, double d )
{
cursor( index ).putDouble( d );
}
/**
* @inheritDoc
*/
public char getChar( int index )
{
return cursor( index ).getChar();
}
/**
* @inheritDoc
*/
public void putChar( int index, char c )
{
cursor( index ).putChar( c );
}
private class CursorImpl implements Cursor
{
private int index;
private final CursorListener listener;
private Node componentNode;
// Index of start of current component.
private int componentIndex;
// Cursor within current component.
private ByteArray.Cursor componentCursor;
public CursorImpl()
{
this( 0, null );
}
public CursorImpl( int index )
{
this( index, null );
}
public CursorImpl( CursorListener listener )
{
this( 0, listener );
}
public CursorImpl( int index, CursorListener listener )
{
this.index = index;
this.listener = listener;
}
/**
* @inheritDoc
*/
public int getIndex()
{
return index;
}
/**
* @inheritDoc
*/
public void setIndex( int index )
{
checkBounds( index, 0 );
this.index = index;
}
/**
* @inheritDoc
*/
public void skip( int length )
{
setIndex( index + length );
}
/**
* @inheritDoc
*/
public ByteArray slice( int length )
{
CompositeByteArray slice = new CompositeByteArray( byteArrayFactory );
int remaining = length;
while ( remaining > 0 )
{
prepareForAccess( remaining );
int componentSliceSize = Math.min( remaining, componentCursor.getRemaining() );
ByteArray componentSlice = componentCursor.slice( componentSliceSize );
slice.addLast( componentSlice );
index += componentSliceSize;
remaining -= componentSliceSize;
}
return slice;
}
/**
* @inheritDoc
*/
public ByteOrder order()
{
return CompositeByteArray.this.order();
}
private void prepareForAccess( int accessSize )
{
// Handle removed node. Do this first so we can remove the reference
// even if bounds checking fails.
if ( componentNode != null && componentNode.isRemoved() )
{
componentNode = null;
componentCursor = null;
}
// Bounds checks
checkBounds( index, accessSize );
// Remember the current node so we can later tell whether or not we
// need to create a new cursor.
Node oldComponentNode = componentNode;
// Handle missing node.
if ( componentNode == null )
{
int basMidpoint = ( last() - first() ) / 2 + first();
if ( index <= basMidpoint )
{
// Search from the start.
componentNode = bas.getFirst();
componentIndex = first();
if ( listener != null )
{
listener.enteredFirstComponent( componentIndex, componentNode.getByteArray() );
}
}
else
{
// Search from the end.
componentNode = bas.getLast();
componentIndex = last() - componentNode.getByteArray().last();
if ( listener != null )
{
listener.enteredLastComponent( componentIndex, componentNode.getByteArray() );
}
}
}
// Go back, if necessary.
while ( index < componentIndex )
{
componentNode = componentNode.getPreviousNode();
componentIndex -= componentNode.getByteArray().last();
if ( listener != null )
{
listener.enteredPreviousComponent( componentIndex, componentNode.getByteArray() );
}
}
// Go forward, if necessary.
while ( index >= componentIndex + componentNode.getByteArray().length() )
{
componentIndex += componentNode.getByteArray().last();
componentNode = componentNode.getNextNode();
if ( listener != null )
{
listener.enteredNextComponent( componentIndex, componentNode.getByteArray() );
}
}
// Update the cursor.
int internalComponentIndex = index - componentIndex;
if ( componentNode == oldComponentNode )
{
// Move existing cursor.
componentCursor.setIndex( internalComponentIndex );
}
else
{
// Create new cursor.
componentCursor = componentNode.getByteArray().cursor( internalComponentIndex );
}
}
/**
* @inheritDoc
*/
public int getRemaining()
{
return last() - index + 1;
}
/**
* @inheritDoc
*/
public boolean hasRemaining()
{
return getRemaining() > 0;
}
/**
* @inheritDoc
*/
public byte get()
{
prepareForAccess( 1 );
byte b = componentCursor.get();
index += 1;
return b;
}
/**
* @inheritDoc
*/
public void put( byte b )
{
prepareForAccess( 1 );
componentCursor.put( b );
index += 1;
}
/**
* @inheritDoc
*/
public void get( IoBuffer bb )
{
while ( bb.hasRemaining() )
{
int remainingBefore = bb.remaining();
prepareForAccess( remainingBefore );
componentCursor.get( bb );
int remainingAfter = bb.remaining();
// Advance index by actual amount got.
int chunkSize = remainingBefore - remainingAfter;
index += chunkSize;
}
}
/**
* @inheritDoc
*/
public void put( IoBuffer bb )
{
while ( bb.hasRemaining() )
{
int remainingBefore = bb.remaining();
prepareForAccess( remainingBefore );
componentCursor.put( bb );
int remainingAfter = bb.remaining();
// Advance index by actual amount put.
int chunkSize = remainingBefore - remainingAfter;
index += chunkSize;
}
}
/**
* @inheritDoc
*/
public short getShort()
{
prepareForAccess( 2 );
if ( componentCursor.getRemaining() >= 4 )
{
short s = componentCursor.getShort();
index += 2;
return s;
}
else
{
byte b0 = get();
byte b1 = get();
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
return ( short ) ( ( b0 << 8 ) | ( b1 << 0 ) );
}
else
{
return ( short ) ( ( b1 << 8 ) | ( b0 << 0 ) );
}
}
}
/**
* @inheritDoc
*/
public void putShort( short s )
{
prepareForAccess( 2 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putShort( s );
index += 2;
}
else
{
byte b0;
byte b1;
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
b0 = ( byte ) ( ( s >> 8 ) & 0xff );
b1 = ( byte ) ( ( s >> 0 ) & 0xff );
}
else
{
b0 = ( byte ) ( ( s >> 0 ) & 0xff );
b1 = ( byte ) ( ( s >> 8 ) & 0xff );
}
put( b0 );
put( b1 );
}
}
/**
* @inheritDoc
*/
public int getInt()
{
prepareForAccess( 4 );
if ( componentCursor.getRemaining() >= 4 )
{
int i = componentCursor.getInt();
index += 4;
return i;
}
else
{
byte b0 = get();
byte b1 = get();
byte b2 = get();
byte b3 = get();
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
return ( ( b0 << 24 ) | ( b1 << 16 ) | ( b2 << 8 ) | ( b3 << 0 ) );
}
else
{
return ( ( b3 << 24 ) | ( b2 << 16 ) | ( b1 << 8 ) | ( b0 << 0 ) );
}
}
}
/**
* @inheritDoc
*/
public void putInt( int i )
{
prepareForAccess( 4 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putInt( i );
index += 4;
}
else
{
byte b0;
byte b1;
byte b2;
byte b3;
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
b0 = ( byte ) ( ( i >> 24 ) & 0xff );
b1 = ( byte ) ( ( i >> 16 ) & 0xff );
b2 = ( byte ) ( ( i >> 8 ) & 0xff );
b3 = ( byte ) ( ( i >> 0 ) & 0xff );
}
else
{
b0 = ( byte ) ( ( i >> 0 ) & 0xff );
b1 = ( byte ) ( ( i >> 8 ) & 0xff );
b2 = ( byte ) ( ( i >> 16 ) & 0xff );
b3 = ( byte ) ( ( i >> 24 ) & 0xff );
}
put( b0 );
put( b1 );
put( b2 );
put( b3 );
}
}
/**
* @inheritDoc
*/
public long getLong()
{
prepareForAccess( 8 );
if ( componentCursor.getRemaining() >= 4 )
{
long l = componentCursor.getLong();
index += 8;
return l;
}
else
{
byte b0 = get();
byte b1 = get();
byte b2 = get();
byte b3 = get();
byte b4 = get();
byte b5 = get();
byte b6 = get();
byte b7 = get();
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
return ( ( b0 & 0xffL ) << 56 ) | ( ( b1 & 0xffL ) << 48 ) | ( ( b2 & 0xffL ) << 40 )
| ( ( b3 & 0xffL ) << 32 ) | ( ( b4 & 0xffL ) << 24 ) | ( ( b5 & 0xffL ) << 16 )
| ( ( b6 & 0xffL ) << 8 ) | ( ( b7 & 0xffL ) << 0 );
}
else
{
return ( ( b7 & 0xffL ) << 56 ) | ( ( b6 & 0xffL ) << 48 ) | ( ( b5 & 0xffL ) << 40 )
| ( ( b4 & 0xffL ) << 32 ) | ( ( b3 & 0xffL ) << 24 ) | ( ( b2 & 0xffL ) << 16 )
| ( ( b1 & 0xffL ) << 8 ) | ( ( b0 & 0xffL ) << 0 );
}
}
}
/**
* @inheritDoc
*/
public void putLong( long l )
{
//TODO: see if there is some optimizing that can be done here
prepareForAccess( 8 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putLong( l );
index += 8;
}
else
{
byte b0;
byte b1;
byte b2;
byte b3;
byte b4;
byte b5;
byte b6;
byte b7;
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
b0 = ( byte ) ( ( l >> 56 ) & 0xff );
b1 = ( byte ) ( ( l >> 48 ) & 0xff );
b2 = ( byte ) ( ( l >> 40 ) & 0xff );
b3 = ( byte ) ( ( l >> 32 ) & 0xff );
b4 = ( byte ) ( ( l >> 24 ) & 0xff );
b5 = ( byte ) ( ( l >> 16 ) & 0xff );
b6 = ( byte ) ( ( l >> 8 ) & 0xff );
b7 = ( byte ) ( ( l >> 0 ) & 0xff );
}
else
{
b0 = ( byte ) ( ( l >> 0 ) & 0xff );
b1 = ( byte ) ( ( l >> 8 ) & 0xff );
b2 = ( byte ) ( ( l >> 16 ) & 0xff );
b3 = ( byte ) ( ( l >> 24 ) & 0xff );
b4 = ( byte ) ( ( l >> 32 ) & 0xff );
b5 = ( byte ) ( ( l >> 40 ) & 0xff );
b6 = ( byte ) ( ( l >> 48 ) & 0xff );
b7 = ( byte ) ( ( l >> 56 ) & 0xff );
}
put( b0 );
put( b1 );
put( b2 );
put( b3 );
put( b4 );
put( b5 );
put( b6 );
put( b7 );
}
}
/**
* @inheritDoc
*/
public float getFloat()
{
prepareForAccess( 4 );
if ( componentCursor.getRemaining() >= 4 )
{
float f = componentCursor.getFloat();
index += 4;
return f;
}
else
{
int i = getInt();
return Float.intBitsToFloat( i );
}
}
/**
* @inheritDoc
*/
public void putFloat( float f )
{
prepareForAccess( 4 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putFloat( f );
index += 4;
}
else
{
int i = Float.floatToIntBits( f );
putInt( i );
}
}
/**
* @inheritDoc
*/
public double getDouble()
{
prepareForAccess( 8 );
if ( componentCursor.getRemaining() >= 4 )
{
double d = componentCursor.getDouble();
index += 8;
return d;
}
else
{
long l = getLong();
return Double.longBitsToDouble( l );
}
}
/**
* @inheritDoc
*/
public void putDouble( double d )
{
prepareForAccess( 8 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putDouble( d );
index += 8;
}
else
{
long l = Double.doubleToLongBits( d );
putLong( l );
}
}
/**
* @inheritDoc
*/
public char getChar()
{
prepareForAccess( 2 );
if ( componentCursor.getRemaining() >= 4 )
{
char c = componentCursor.getChar();
index += 2;
return c;
}
else
{
byte b0 = get();
byte b1 = get();
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
return ( char ) ( ( b0 << 8 ) | ( b1 << 0 ) );
}
else
{
return ( char ) ( ( b1 << 8 ) | ( b0 << 0 ) );
}
}
}
/**
* @inheritDoc
*/
public void putChar( char c )
{
prepareForAccess( 2 );
if ( componentCursor.getRemaining() >= 4 )
{
componentCursor.putChar( c );
index += 2;
}
else
{
byte b0;
byte b1;
if ( order.equals( ByteOrder.BIG_ENDIAN ) )
{
b0 = ( byte ) ( ( c >> 8 ) & 0xff );
b1 = ( byte ) ( ( c >> 0 ) & 0xff );
}
else
{
b0 = ( byte ) ( ( c >> 0 ) & 0xff );
b1 = ( byte ) ( ( c >> 8 ) & 0xff );
}
put( b0 );
put( b1 );
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy