You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ad...@apache.org on 2004/06/10 23:22:31 UTC
svn commit: rev 21037 - incubator/directory/snickers/branches/ber-decoder/ber-codec/src/java/org/apache/snickers/ber
Author: adc
Date: Thu Jun 10 14:22:30 2004
New Revision: 21037
Modified:
incubator/directory/snickers/branches/ber-decoder/ber-codec/src/java/org/apache/snickers/ber/BERDecoder.java
Log:
Quick check in for Alex to look at.
Modified: incubator/directory/snickers/branches/ber-decoder/ber-codec/src/java/org/apache/snickers/ber/BERDecoder.java
==============================================================================
--- incubator/directory/snickers/branches/ber-decoder/ber-codec/src/java/org/apache/snickers/ber/BERDecoder.java (original)
+++ incubator/directory/snickers/branches/ber-decoder/ber-codec/src/java/org/apache/snickers/ber/BERDecoder.java Thu Jun 10 14:22:30 2004
@@ -14,164 +14,116 @@
* limitations under the License.
*
*/
-package org.apache.snickers.ber ;
+package org.apache.snickers.ber;
+import java.nio.ByteBuffer;
+import java.util.EmptyStackException;
-import java.util.Stack ;
-
-import java.nio.ByteBuffer ;
-
-import org.apache.commons.lang.ArrayUtils ;
-
-import org.apache.commons.codec.DecoderException ;
-import org.apache.commons.codec.stateful.DecoderMonitor ;
-import org.apache.commons.codec.stateful.DecoderCallback ;
-import org.apache.commons.codec.stateful.StatefulDecoder ;
-import org.apache.commons.codec.stateful.DecoderMonitorAdapter ;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.stateful.DecoderCallback;
+import org.apache.commons.codec.stateful.DecoderMonitor;
+import org.apache.commons.codec.stateful.StatefulDecoder;
/**
- * A decoder that decodes BER encoded bytes to Tag Value Length (TLV) tuples.
- * This decoder is a low level event based parser which operates in a fashion
- * similar to the way SAX works except the elements of concern are the tag,
+ * A decoder that decodes BER encoded bytes to Tag Value Length (TLV) tuples.
+ * This decoder is a low level event based parser which operates in a fashion
+ * similar to the way SAX works except the elements of concern are the tag,
* length, and value entities. The decoder is a state machine which processes
* input as it is made available.
- * <p>
- * A Stack is used to track the state of the decoder between decode calls. It
- * maintains the nesting of TLV tuples. Rather than creating new TLV tuple
- * instances every time a single tuple is reused for primitive types and new
+ * <p/>
+ * A Stack is used to track the state of the decoder between decode calls. It
+ * maintains the nesting of TLV tuples. Rather than creating new TLV tuple
+ * instances every time a single tuple is reused for primitive types and new
* tlv tuples are cloned for constructed types which are pushed onto the stack.
- * The tuple fed to the callback must therefore be used very carefully - its
- * values must be copied to prevent their loss if they are to be used later
+ * The tuple fed to the callback must therefore be used very carefully - its
+ * values must be copied to prevent their loss if they are to be used later
* after the callback invokation has returned.
* </p>
- * <p>
+ * <p/>
* Note that all tuples are not created equal. Constructed TLVs nesting others
- * will have null value members or empty buffers. Only TLV tuples of primitive
- * types or the leaf TLV tuples of the TLV tuple tree will contain non null
- * values. Therefore the nature of a TLV tuple should be investigated by
+ * will have null value members or empty buffers. Only TLV tuples of primitive
+ * types or the leaf TLV tuples of the TLV tuple tree will contain non null
+ * values. Therefore the nature of a TLV tuple should be investigated by
* callbacks before attempting to interpret their values. Also this decoder
* chunks value data returning it in parts rather than in one complete peice
* in the end. The value of the TLV Tuple returned is the part of the value
* that was read from the input fed into the decoder. These 'chunks' returned
* by callback makes it so there are no size limits to the value of a TLV. Again
- * to reiterate chunking on values is only performed on primitive TLV Tuple
- * types.
+ * to reiterate chunking on values is only performed on primitive TLV Tuple
+ * types.
* </p>
- *
- * @author <a href="mailto:directory-dev@incubator.apache.org">
- * Apache Directory Project</a>
+ *
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
-public class BERDecoder implements StatefulDecoder, DecoderCallback
+public class BERDecoder implements StatefulDecoder
{
- /** empty byte buffer to be reused */
- private static final ByteBuffer EMPTY_BUFFER =
- ByteBuffer.wrap( ArrayUtils.EMPTY_BYTE_ARRAY ) ;
- /** the callback used by this decoder */
- private static final BERDecoderCallback DEFAULT_CALLBACK =
- new BERDecoderCallbackAdapter() ;
- /** the monitor used by this decoder */
- private static final DecoderMonitor DEFAULT_MONITOR =
- new DecoderMonitorAdapter() ;
-
- /** this decoder's callback */
- private BERDecoderCallback cb = DEFAULT_CALLBACK ;
- /** the monitor used by this decoder */
- private DecoderMonitor monitor = DEFAULT_MONITOR ;
-
- /** the single TLV tuple used by this decoder */
- private final Tuple tlv = new Tuple() ;
-
- /** a decoder used to decode tag octets */
- private final TagDecoder tagDecoder = new TagDecoder() ;
- /** a decoder used to decode length octets */
- private final LengthDecoder lengthDecoder = new LengthDecoder() ;
-
- /** stack of nested/constructed TLV tuples */
- private final Stack tlvStack = new Stack() ;
-
- /** the state of this decoder */
- private BERDecoderState state = BERDecoderState.getStartState() ;
-
-
+ public final State TAG_STATE = new TagState();
+ public final State LENGTH_STATE = new LengthState();
+ public final State VALUE_STATE = new ValueState();
+
+ /**
+ * the single TLV tuple used by this decoder
+ */
+ Tuple tlv;
+
+ Tag tag = new Tag();
+ Length length = new Length();
+
+ /**
+ * stack of nested/constructed TLV tuples
+ */
+ final TupleStack stack;
+
+ /**
+ * the state of this decoder
+ */
+ private State state = TAG_STATE;
+
+
/**
* Creates a stateful BER decoder which limits the tuple's value size.
*/
public BERDecoder()
{
- tagDecoder.setCallback( this ) ;
- lengthDecoder.setCallback( this ) ;
+ stack = new TupleStack( this );
}
-
-
- // ------------------------------------------------------------------------
- // StatefulDecoder Methods
- // ------------------------------------------------------------------------
-
/**
- * Expects a ByteBuffer containing BER encoded data.
- *
- * @see org.apache.commons.codec.stateful.StatefulDecoder#decode(
- * java.lang.Object)
- * @throws ClassCastException if the encoded argument is not a ByteBuffer
- * @throws IllegalArgumentException if the buffer is null or empty
+ * Obtain the current incomplete tuple that is on the top of the stack.
+ *
+ * @return the current incomplete tuple that is on the top of the stack
+ */
+ public Tuple getCurrentTuple()
+ {
+ return tlv;
+// try
+// {
+// return stack.peek();
+// }
+// catch ( EmptyStackException ese )
+// {
+// return tlv;
+// }
+ }
+
+ /**
+ * Obtain the current state of the decoder.
+ *
+ * @return the current state of the decoder
*/
- public void decode( Object encoded ) throws DecoderException
+ public State getState()
{
- ByteBuffer buf = ( ByteBuffer ) encoded ;
-
- /* --------------------------------------------------------------------
- Handle any unusual input by informing the monitor.
- ------------------------------------------------------------------ */
-
- if ( buf == null && monitor != null )
- {
- String msg = "ignoring null argument to decode()" ;
- monitor.warning( this, new IllegalArgumentException( msg ) ) ;
- return ;
- }
-
- if ( buf.remaining() == 0 && monitor != null )
- {
- String msg = "ignoring empty buffer" ;
- monitor.warning( this, new IllegalArgumentException( msg ) ) ;
- return ;
- }
-
- /*
- * This loop is used instead of costly recursion. This requires each
- * of the statewise decode methods to process bytes from the buffer. If
- * they can process enough to switch state they do and return
- * immediately. This loop makes sure the next processing state is
- * handled if there is more data for that state.
- */
- while ( buf.hasRemaining() )
- {
- switch( state.getValue() )
- {
- case( BERDecoderState.TAG_VAL ):
- tagDecoder.decode( buf ) ;
- break ;
- case( BERDecoderState.LENGTH_VAL ):
- lengthDecoder.decode( buf ) ;
- break ;
- case( BERDecoderState.VALUE_VAL ):
- decodeValue( buf ) ;
- break ;
- }
- }
+ return state;
}
-
/* (non-Javadoc)
- * @see org.apache.commons.codec.stateful.StatefulDecoder#setCallback(
- * org.apache.commons.codec.stateful.DecoderCallback)
+ * @see StatefulDecoder#setCallback(DecoderCallback)
*/
public void setCallback( DecoderCallback cb )
{
- this.cb = ( BERDecoderCallback ) cb ;
+ stack.setCallback( cb );
}
@@ -181,355 +133,179 @@
*/
public void setDecoderMonitor( DecoderMonitor monitor )
{
- this.monitor = monitor ;
+ stack.setMonitor( monitor );
}
-
-
- // ------------------------------------------------------------------------
- // State Based Decode Methods
- // ------------------------------------------------------------------------
-
-
+
/**
- * Extracts the value portion from the buffer for a primitive type.
- *
- * @param buf the byte byffer containing BER encoded data
+ * Expects a ByteBuffer containing BER encoded data.
+ *
+ * @throws ClassCastException if the encoded argument is not a ByteBuffer
+ * @throws IllegalArgumentException if the buffer is null or empty
+ * @see org.apache.commons.codec.stateful.StatefulDecoder#decode(java.lang.Object)
*/
- private void decodeValue( ByteBuffer buf )
+ public void decode( Object encoded ) throws DecoderException
{
- int needToRead = Length.UNDEFINED ;
+ if ( encoded == null ) throw new IllegalArgumentException( "Parameter 'encoded' is null" );
+ ByteBuffer buffer = (ByteBuffer) encoded;
/*
- * setup to start decoding the value by figuring how much we need to
- * read at this point - previous reads of the value may have already
- * occurred.
+ * This loop is used instead of costly recursion. This requires each
+ * of the statewise decode methods to process bytes from the buffer. If
+ * they can process enough to switch state they do and return
+ * immediately. This loop makes sure the next processing state is
+ * handled if there is more data for that state.
*/
- if ( tlv.valueIndex == Length.UNDEFINED )
- {
- needToRead = tlv.length ;
- }
- else
+ while ( buffer.hasRemaining() )
{
- needToRead = tlv.length - tlv.valueIndex ;
+ state.decode( buffer );
}
-
- /*
- * check if we have the remainder of the value to complete the
- * TLV within the current buffer - if so we read all of it
+ }
+
+
+ public abstract class State
+ {
+
+ /**
+ * Decodes a peice of encoded data. The nature of this call, synchronous
+ * verses asynchonous, with respect to driving the actual decoding of the
+ * encoded data argument is determined by an implementation. A return from
+ * this method does not guarrantee any callbacks: zero or more callbacks
+ * may occur during this call.
+ *
+ * @param encoded an object representing a peice of encoded data
*/
- if ( buf.remaining() >= needToRead )
- {
- tlv.valueChunk = ( ByteBuffer ) buf.slice().limit( needToRead ) ;
- buf.position( buf.position() + needToRead ) ;
- tlv.valueIndex = tlv.length ;
- tlv.index += tlv.length ;
-
- cb.partialValueDecoded( tlv ) ;
- fireDecodeOccurred( tlv ) ;
- updateStack( needToRead ) ;
- tlv.clear() ;
- state = BERDecoderState.TAG ;
- }
-
- /*
- * the buffer does not contain the rest of the value we need in order
- * to complete the current TLV - the value is fragmented so we read
- * what we can and update indices by that amount.
+ protected abstract void decode( ByteBuffer encoded ) throws DecoderException;
+
+ }
+
+ protected class TagState extends State
+ {
+ /**
+ * Decodes a peice of encoded data. The nature of this call, synchronous
+ * verses asynchonous, with respect to driving the actual decoding of the
+ * encoded data argument is determined by an implementation. A return from
+ * this method does not guarrantee any callbacks: zero or more callbacks
+ * may occur during this call.
+ *
+ * @param buf an object representing a peice of encoded data
*/
- else
+ protected void decode( ByteBuffer buf ) throws DecoderException
{
- if ( tlv.valueIndex == Length.UNDEFINED )
+ while ( buf.hasRemaining() )
{
- tlv.valueIndex = 0 ;
+ byte octet = buf.get();
+ tag.add( octet );
+
+ if ( tag.isFixated() )
+ {
+ tlv = new Tuple();
+
+ tlv.rawTag = tag.getRawTag();
+ tlv.id = tag.getId();
+ tlv.isPrimitive = tag.isPrimitive();
+ tlv.typeClass = tag.getTypeClass();
+
+ state = LENGTH_STATE;
+
+ tag.clear();
+
+ return;
+ }
}
-
- int remaining = buf.remaining() ;
- tlv.valueChunk = buf.slice() ;
- buf.position( buf.limit() ) ;
- tlv.valueIndex += remaining ;
- tlv.index +=remaining ;
-
- cb.partialValueDecoded( tlv ) ;
- updateStack( remaining ) ;
+
+ state = TAG_STATE;
}
}
-
-
-
- /* (non-Javadoc)
- * @see org.apache.commons.codec.stateful.DecoderCallback#decodeOccurred(
- * org.apache.commons.codec.stateful.StatefulDecoder, java.lang.Object)
- */
- public void decodeOccurred( StatefulDecoder decoder, Object decoded )
+
+ protected class LengthState extends State
{
- if ( decoder == tagDecoder )
- {
- Tag tag = ( Tag ) decoded ;
- tlv.rawTag = tag.getRawTag() ;
- tlv.id = tag.getId() ;
- tlv.isPrimitive = tag.isPrimitive() ;
- tlv.typeClass = tag.getTypeClass() ;
- tlv.index = tag.size() ;
-
- fireTagDecoded() ;
- updateStack( tag.size() ) ;
- state = state.getNext( tag.isPrimitive() ) ;
- }
- else if ( decoder == lengthDecoder )
+ /**
+ * Decodes a peice of encoded data. The nature of this call, synchronous
+ * verses asynchonous, with respect to driving the actual decoding of the
+ * encoded data argument is determined by an implementation. A return from
+ * this method does not guarrantee any callbacks: zero or more callbacks
+ * may occur during this call.
+ *
+ * @param buf an object representing a peice of encoded data
+ */
+ protected void decode( ByteBuffer buf ) throws DecoderException
{
- Length length = ( Length ) decoded ;
- tlv.length = length.getLength() ;
-
- if ( tlv.length == Length.INDEFINATE )
- {
- tlv.index = Length.INDEFINATE ;
- tlv.valueIndex = Length.INDEFINATE ;
- }
- else
- {
- tlv.index += length.size() ;
- }
-
- fireLengthDecoded() ;
- updateStack( length.size() ) ;
-
- if ( ! tlv.isPrimitive )
+
+ while ( buf.hasRemaining() )
{
- if ( tlv.isIndefinate() || tlv.length > 0 )
- {
- tlvStack.push( tlv.clone() ) ;
- }
- else
+ byte octet = buf.get();
+ length.add( octet );
+
+ if ( length.isFixated() )
{
- state = BERDecoderState.VALUE ;
- fireDecodeOccurred( tlv ) ;
+ tlv.length = length.getLength();
+
+ stack.push( tlv );
+
+ if ( tlv.isIndefiniteTerminator() || tlv.isIndefinite() || tlv.getLength() == 0 || !tlv.isPrimitive() )
+ {
+ state = TAG_STATE;
+ }
+ else
+ {
+ state = VALUE_STATE;
+ }
+
+ length.clear();
+
+ return;
}
-
- state = BERDecoderState.TAG ;
- tlv.clear() ;
}
- else if ( tlv.isIndefinateTerminator() )
- {
- return ;
- }
- else if ( tlv.length > 0 )
- {
- state = BERDecoderState.VALUE ;
- }
- else
- {
- state = BERDecoderState.VALUE ;
- tlv.valueChunk = EMPTY_BUFFER ;
- cb.partialValueDecoded( tlv ) ;
- fireDecodeOccurred( tlv ) ;
- state = BERDecoderState.TAG ;
- }
- }
- else
- {
- throw new IllegalArgumentException( "unrecognized decoder" ) ;
- }
- }
-
-
- // ------------------------------------------------------------------------
- // private utility methods
- // ------------------------------------------------------------------------
-
-
- /**
- * Fires a tag decoded event by making the appropriate calls to the
- * callback and the monitor. If the monitor is a BERDecoderMonitor with
- * extended reporting, then those methods are invoked.
- *
- * Also as a side-effect this method clears the tag buffer once it has
- * finished notifying the monitor and calling the callback.
- */
- private void fireTagDecoded()
- {
- if ( cb != null )
- {
- cb.tagDecoded( tlv ) ;
- }
- if ( monitor != null && monitor instanceof BERDecoderMonitor )
- {
- BERDecoderMonitor berMonitor = ( BERDecoderMonitor ) monitor ;
- berMonitor.tagDecoded( tlv ) ;
- }
- }
-
-
- /**
- * Fires a length decoded event by making the appropriate calls to the
- * callback and the monitor. If the monitor is a BERDecoderMonitor with
- * extended reporting, then those methods are invoked.
- *
- * Also as a side-effect this method clears the length buffer once it has
- * finished notifying the monitor and calling the callback.
- */
- private void fireLengthDecoded()
- {
- if ( cb != null )
- {
- cb.lengthDecoded( tlv ) ;
- }
-
- if ( monitor != null && monitor instanceof BERDecoderMonitor )
- {
- BERDecoderMonitor berMonitor = ( BERDecoderMonitor ) monitor ;
- berMonitor.lengthDecoded( tlv ) ;
- }
- }
-
-
- /**
- * Fires a complete TLV decoded event by making the appropriate calls to
- * the callback and the monitor.
- */
- private void fireDecodeOccurred( Tuple tlv )
- {
- if ( cb != null )
- {
- cb.decodeOccurred( this, tlv ) ;
- }
-
- if ( monitor != null )
- {
- monitor.callbackOccured( this, cb, tlv ) ;
+ state = LENGTH_STATE;
}
}
-
- /**
- * Increments the indices of constructed TLV's within the TLV Stack.
- *
- * @param increment the amount to increment indices by.
- */
- private void updateStack( int increment )
+ protected class ValueState extends State
{
- for ( int ii = 0; ii < tlvStack.size(); ii++ )
- {
- Tuple t = ( Tuple ) tlvStack.get( ii ) ;
-
- if ( t.isIndefinate() )
- {
- continue ;
- }
-
- t.index += increment ;
-
- if ( t.valueIndex == Length.UNDEFINED )
- {
- t.valueIndex = 0 ;
- }
-
- t.valueIndex += increment ;
- }
-
- if ( tlvStack.isEmpty() )
- {
- return ;
- }
-
- do
+ /**
+ * Decodes a peice of encoded data. The nature of this call, synchronous
+ * verses asynchonous, with respect to driving the actual decoding of the
+ * encoded data argument is determined by an implementation. A return from
+ * this method does not guarrantee any callbacks: zero or more callbacks
+ * may occur during this call.
+ *
+ * @param buffer an object representing a peice of encoded data
+ */
+ protected void decode( ByteBuffer buffer ) throws DecoderException
{
- Tuple top = ( Tuple ) tlvStack.peek() ;
-
- if ( top.isIndefinate() && tlv.isIndefinateTerminator() )
- {
- tlvStack.pop() ;
- state = BERDecoderState.VALUE ;
- fireDecodeOccurred( top ) ;
- state = BERDecoderState.TAG ;
- }
- else if ( top.isIndefinate() )
- {
- break ;
- }
- else if ( top.valueIndex >= top.length )
- {
- tlvStack.pop() ;
- state = BERDecoderState.VALUE ;
- fireDecodeOccurred( top ) ;
- state = BERDecoderState.TAG ;
+ /*
+ * setup to start decoding the value by figuring how much we need to
+ * read at this point - previous reads of the value may have already
+ * occurred.
+ */
+ int needToRead = tlv.remaining();
+
+ if ( buffer.remaining() >= needToRead )
+ {
+ /*
+ * check if we have the remainder of the value to complete the
+ * TLV within the current buffer - if so we read all of it
+ */
+
+ stack.contents( (ByteBuffer) buffer.slice().limit( needToRead ) );
+ buffer.position( buffer.position() + needToRead );
+
+ state = TAG_STATE;
}
else
{
- break ;
- }
-
- } while( tlvStack.size() > 0 ) ;
- }
+ /*
+ * the buffer does not contain the rest of the value we need in order
+ * to complete the current TLV - the value is fragmented so we read
+ * what we can and update indices by that amount.
+ */
+ stack.contents( (ByteBuffer) buffer.slice() );
+ buffer.position( buffer.limit() );
- /*
-
- Why copy the raw tag here when we can maintain our own stack in the
- digester that does the pushing and popping instead? Keep this here
- until we decide what to do.
-
- public int[] getTagNestingPattern()
- {
- int stackSz = tlvStack.size() ;
- int[] pattern = new int[stackSz+1] ;
- pattern[stackSz] = tlv.rawTag ;
-
- for ( int ii = 0; ii < stackSz; ii++ )
- {
- pattern[ii] = ( ( Tuple ) tlvStack.get( ii ) ).rawTag ;
- }
-
- return pattern ;
- }
- */
-
-
- // ------------------------------------------------------------------------
- // Methods used for testing
- // ------------------------------------------------------------------------
-
-
- /**
- * Gets the current state of this BERDecoder. Used only for debugging and
- * testing.
- *
- * @return the state enum
- */
- BERDecoderState getState()
- {
- return state ;
- }
-
-
- /**
- * Gets a cloned copy of the current tuple. Used only for debugging and
- * testing.
- *
- * @return a clone of the current tlv
- */
- Tuple getCurrentTuple()
- {
- return ( Tuple ) tlv.clone() ;
- }
-
-
- /**
- * Gets a deep copy of the constructed tuple stack. Used only for debugging
- * and testing.
- *
- * @return a deep copy of the tuple stack
- */
- Stack getTupleStack()
- {
- Stack stack = new Stack() ;
-
- for ( int ii = 0; ii < tlvStack.size(); ii++ )
- {
- Tuple t = ( Tuple ) tlvStack.get( ii ) ;
- stack.add( t.clone() ) ;
+ state = VALUE_STATE;
+ }
}
-
- return stack ;
}
}