View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.util.byteaccess;
21  
22  
23  import java.nio.ByteOrder;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  
28  import org.apache.mina.core.buffer.IoBuffer;
29  import org.apache.mina.util.byteaccess.ByteArrayList.Node;
30  
31  
32  /**
33   * A ByteArray composed of other ByteArrays. Optimised for fast relative access
34   * via cursors. Absolute access methods are provided, but may perform poorly.
35   *
36   * TODO: Write about laziness of cursor implementation - how movement doesn't
37   * happen until actual get/put.
38   * 
39   * @author The Apache MINA Project (dev@mina.apache.org)
40   */
41  public final class CompositeByteArray extends AbstractByteArray {
42  
43      /**
44       * Allows for efficient detection of component boundaries when using a cursor.
45       *
46       * TODO: Is this interface right?
47       */
48      public interface CursorListener {
49  
50          /**
51           * Called when the first component in the composite is entered by the cursor.
52           */
53          public void enteredFirstComponent( int componentIndex, ByteArray component );
54  
55  
56          /**
57           * Called when the next component in the composite is entered by the cursor.
58           */
59          public void enteredNextComponent( int componentIndex, ByteArray component );
60  
61  
62          /**
63           * Called when the previous component in the composite is entered by the cursor.
64           */
65          public void enteredPreviousComponent( int componentIndex, ByteArray component );
66  
67  
68          /**
69           * Called when the last component in the composite is entered by the cursor.
70           */
71          public void enteredLastComponent( int componentIndex, ByteArray component );
72      }
73  
74      /**
75       * Stores the underlying <code>ByteArray</code>s.
76       */
77      private final ByteArrayList bas = new ByteArrayList();
78  
79      /**
80       * The byte order for data in the buffer
81       */
82      private ByteOrder order;
83  
84      /**
85       * May be used in <code>getSingleIoBuffer</code>. Optional.
86       */
87      private final ByteArrayFactory byteArrayFactory;
88  
89      /**
90       * Creates a new instance of CompositeByteArray.
91       */
92      public CompositeByteArray() {
93          this( null );
94      }
95  
96      /**
97       * 
98       * Creates a new instance of CompositeByteArray.
99       *
100      * @param byteArrayFactory
101      *  The factory used to create the ByteArray objects
102      */
103     public CompositeByteArray( ByteArrayFactory byteArrayFactory ) {
104         this.byteArrayFactory = byteArrayFactory;
105     }
106 
107     /**
108      * Returns the first {@link ByteArray} in the list
109      *
110      * @return
111      *  The first ByteArray in the list
112      */
113     public ByteArray getFirst() {
114         if ( bas.isEmpty() ) {
115             return null;
116         }
117 
118         return bas.getFirst().getByteArray();
119     }
120 
121     /**
122      * Adds the specified {@link ByteArray} to the first
123      * position in the list
124      *
125      * @param ba
126      *  The ByteArray to add to the list
127      */
128     public void addFirst( ByteArray ba ) {
129         addHook( ba );
130         bas.addFirst( ba );
131     }
132 
133     /**
134      * Remove the first {@link ByteArray} in the list
135      *
136      * @return
137      *  The first ByteArray in the list
138      */
139     public ByteArray removeFirst() {
140         Node node = bas.removeFirst();
141         return node == null ? null : node.getByteArray();
142     }
143 
144 
145     /**
146      * Remove component <code>ByteArray</code>s to the given index (splitting
147      * them if necessary) and returning them in a single <code>ByteArray</code>.
148      * The caller is responsible for freeing the returned object.
149      *
150      * TODO: Document free behaviour more thoroughly.
151      */
152     public ByteArray removeTo( int index ) {
153         if ( index < first() || index > last() ) {
154             throw new IndexOutOfBoundsException();
155         }
156         // Optimisation when removing exactly one component.
157         //        if (index == start() + getFirst().length()) {
158         //            ByteArray component = getFirst();
159         //            removeFirst();
160         //            return component;
161         //        }
162         // Removing
163         CompositeByteArray prefix = new CompositeByteArray( byteArrayFactory );
164         int remaining = index - first();
165         
166         while ( remaining > 0 ) {
167             ByteArray component = removeFirst();
168             
169             if ( component.last() <= remaining ) {
170                 // Remove entire component.
171                 prefix.addLast( component );
172                 remaining -= component.last();
173             } else {
174                 // Remove part of component. Do this by removing entire
175                 // component then readding remaining bytes.
176                 // TODO: Consider using getIoBuffers(), as would avoid
177                 // performance problems for nested ComponentByteArrays.
178                 IoBuffer bb = component.getSingleIoBuffer();
179                 // get the limit of the buffer
180                 int originalLimit = bb.limit();
181                 // set the position to the beginning of the buffer
182                 bb.position( 0 );
183                 // set the limit of the buffer to what is remaining
184                 bb.limit( remaining );
185                 // create a new IoBuffer, sharing the data with 'bb'
186                 IoBuffer bb1 = bb.slice();
187                 // set the position at the end of the buffer
188                 bb.position( remaining );
189                 // gets the limit of the buffer
190                 bb.limit( originalLimit );
191                 // create a new IoBuffer, sharing teh data with 'bb'
192                 IoBuffer bb2 = bb.slice();
193                 // create a new ByteArray with 'bb1'
194                 ByteArray ba1 = new BufferByteArray( bb1 ) {
195                     @Override
196                     public void free() {
197                         // Do not free.  This will get freed 
198                     }
199                 };
200                 
201                 // add the new ByteArray to the CompositeByteArray
202                 prefix.addLast( ba1 );
203                 remaining -= ba1.last();
204                 
205                 // final for anonymous inner class
206                 final ByteArray componentFinal = component; 
207                 ByteArray ba2 = new BufferByteArray( bb2 ) {
208                     @Override
209                     public void free() {
210                         componentFinal.free();
211                     }
212                 };
213                 // add the new ByteArray to the CompositeByteArray
214                 addFirst( ba2 );
215             }
216         }
217         
218         // return the CompositeByteArray
219         return prefix;
220     }
221 
222     /**
223      * Adds the specified {@link ByteArray} to the end of the list
224      *
225      * @param ba
226      *  The ByteArray to add to the end of the list
227      */
228     public void addLast( ByteArray ba ) {
229         addHook( ba );
230         bas.addLast( ba );
231     }
232 
233     /**
234      * Removes the last {@link ByteArray} in the list
235      *
236      * @return
237      *  The ByteArray that was removed
238      */
239     public ByteArray removeLast() {
240         Node node = bas.removeLast();
241         return node == null ? null : node.getByteArray();
242     }
243 
244 
245     /**
246      * @inheritDoc
247      */
248     public void free() {
249         while ( !bas.isEmpty() ) {
250             Node node = bas.getLast();
251             node.getByteArray().free();
252             bas.removeLast();
253         }
254     }
255 
256 
257     private void checkBounds( int index, int accessSize ) {
258         int lower = index;
259         int upper = index + accessSize;
260         
261         if ( lower < first() ) {
262             throw new IndexOutOfBoundsException( "Index " + lower + " less than start " + first() + "." );
263         }
264         
265         if ( upper > last() ) {
266             throw new IndexOutOfBoundsException( "Index " + upper + " greater than length " + last() + "." );
267         }
268     }
269 
270 
271     /**
272      * @inheritDoc
273      */
274     public Iterable<IoBuffer> getIoBuffers() {
275         if ( bas.isEmpty() ) {
276             return Collections.emptyList();
277         }
278         
279         Collection<IoBuffer> result = new ArrayList<IoBuffer>();
280         Node node = bas.getFirst();
281         
282         for ( IoBuffer bb : node.getByteArray().getIoBuffers() ) {
283             result.add( bb );
284         }
285         
286         while ( node.hasNextNode() ) {
287             node = node.getNextNode();
288             
289             for ( IoBuffer bb : node.getByteArray().getIoBuffers() ) {
290                 result.add( bb );
291             }
292         }
293         
294         return result;
295     }
296 
297 
298     /**
299      * @inheritDoc
300      */
301     public IoBuffer getSingleIoBuffer() {
302         if ( byteArrayFactory == null ) {
303             throw new IllegalStateException(
304                 "Can't get single buffer from CompositeByteArray unless it has a ByteArrayFactory." );
305         }
306         
307         if ( bas.isEmpty() ) {
308             ByteArray ba = byteArrayFactory.create( 1 );
309             return ba.getSingleIoBuffer();
310         }
311         
312         int actualLength = last() - first();
313         
314         {
315             Node node = bas.getFirst();
316             ByteArray ba = node.getByteArray();
317             
318             if ( ba.last() == actualLength ) {
319                 return ba.getSingleIoBuffer();
320             }
321         }
322         
323         // Replace all nodes with a single node.
324         ByteArray target = byteArrayFactory.create( actualLength );
325         IoBuffer bb = target.getSingleIoBuffer();
326         Cursor cursor = cursor();
327         cursor.put( bb ); // Copy all existing data into target IoBuffer.
328         
329         while ( !bas.isEmpty() ) {
330             Node node = bas.getLast();
331             ByteArray component = node.getByteArray();
332             bas.removeLast();
333             component.free();
334         }
335         
336         bas.addLast( target );
337         return bb;
338     }
339 
340 
341     /**
342      * @inheritDoc
343      */
344     public Cursor cursor() {
345         return new CursorImpl();
346     }
347 
348 
349     /**
350      * @inheritDoc
351      */
352     public Cursor cursor( int index ) {
353         return new CursorImpl( index );
354     }
355 
356 
357     /**
358      * Get a cursor starting at index 0 (which may not be the start of the
359      * array) and with the given listener.
360      * 
361      * @param listener
362      *  Returns a new {@link Cursor} instance
363      */
364     public Cursor cursor( CursorListener listener ) {
365         return new CursorImpl( listener );
366     }
367 
368 
369     /**
370      * Get a cursor starting at the given index and with the given listener.
371      * 
372      * @param index
373      *  The position of the array to start the Cursor at
374      * @param listener
375      *  The listener for the Cursor that is returned
376      */
377     public Cursor cursor( int index, CursorListener listener ) {
378         return new CursorImpl( index, listener );
379     }
380 
381 
382     /**
383      * @inheritDoc
384      */
385     public ByteArray slice( int index, int length ) {
386         return cursor( index ).slice( length );
387     }
388 
389 
390     /**
391      * @inheritDoc
392      */
393     public byte get( int index ) {
394         return cursor( index ).get();
395     }
396 
397 
398     /**
399      * @inheritDoc
400      */
401     public void put( int index, byte b )
402     {
403         cursor( index ).put( b );
404     }
405 
406 
407     /**
408      * @inheritDoc
409      */
410     public void get( int index, IoBuffer bb )
411     {
412         cursor( index ).get( bb );
413     }
414 
415 
416     /**
417      * @inheritDoc
418      */
419     public void put( int index, IoBuffer bb )
420     {
421         cursor( index ).put( bb );
422     }
423 
424 
425     /**
426      * @inheritDoc
427      */
428     public int first()
429     {
430         return bas.firstByte();
431     }
432 
433 
434     /**
435      * @inheritDoc
436      */
437     public int last()
438     {
439         return bas.lastByte();
440     }
441 
442 
443     /**
444      * This method should be called prior to adding any component
445      * <code>ByteArray</code> to a composite.
446      *
447      * @param ba
448      *  The component to add.
449      */
450     private void addHook( ByteArray ba )
451     {
452         // Check first() is zero, otherwise cursor might not work.
453         // TODO: Remove this restriction?
454         if ( ba.first() != 0 )
455         {
456             throw new IllegalArgumentException( "Cannot add byte array that doesn't start from 0: " + ba.first() );
457         }
458         // Check order.
459         if ( order == null )
460         {
461             order = ba.order();
462         }
463         else if ( !order.equals( ba.order() ) )
464         {
465             throw new IllegalArgumentException( "Cannot add byte array with different byte order: " + ba.order() );
466         }
467     }
468 
469 
470     /**
471      * @inheritDoc
472      */
473     public ByteOrder order()
474     {
475         if ( order == null )
476         {
477             throw new IllegalStateException( "Byte order not yet set." );
478         }
479         return order;
480     }
481 
482 
483     /**
484      * @inheritDoc
485      */
486     public void order( ByteOrder order ) {
487         if ( order == null || !order.equals( this.order ) ) {
488             this.order = order;
489             
490             if ( !bas.isEmpty() ) {
491                 for ( Node node = bas.getFirst(); node.hasNextNode(); node = node.getNextNode() ) {
492                     node.getByteArray().order( order );
493                 }
494             }
495         }
496     }
497 
498 
499     /**
500      * @inheritDoc
501      */
502     public short getShort( int index ) {
503         return cursor( index ).getShort();
504     }
505 
506 
507     /**
508      * @inheritDoc
509      */
510     public void putShort( int index, short s ) {
511         cursor( index ).putShort( s );
512     }
513 
514 
515     /**
516      * @inheritDoc
517      */
518     public int getInt( int index )
519     {
520         return cursor( index ).getInt();
521     }
522 
523 
524     /**
525      * @inheritDoc
526      */
527     public void putInt( int index, int i )
528     {
529         cursor( index ).putInt( i );
530     }
531 
532 
533     /**
534      * @inheritDoc
535      */
536     public long getLong( int index )
537     {
538         return cursor( index ).getLong();
539     }
540 
541 
542     /**
543      * @inheritDoc
544      */
545     public void putLong( int index, long l )
546     {
547         cursor( index ).putLong( l );
548     }
549 
550 
551     /**
552      * @inheritDoc
553      */
554     public float getFloat( int index )
555     {
556         return cursor( index ).getFloat();
557     }
558 
559 
560     /**
561      * @inheritDoc
562      */
563     public void putFloat( int index, float f )
564     {
565         cursor( index ).putFloat( f );
566     }
567 
568 
569     /**
570      * @inheritDoc
571      */
572     public double getDouble( int index )
573     {
574         return cursor( index ).getDouble();
575     }
576 
577 
578     /**
579      * @inheritDoc
580      */
581     public void putDouble( int index, double d )
582     {
583         cursor( index ).putDouble( d );
584     }
585 
586 
587     /**
588      * @inheritDoc
589      */
590     public char getChar( int index )
591     {
592         return cursor( index ).getChar();
593     }
594 
595 
596     /**
597      * @inheritDoc
598      */
599     public void putChar( int index, char c )
600     {
601         cursor( index ).putChar( c );
602     }
603 
604     private class CursorImpl implements Cursor
605     {
606 
607         private int index;
608 
609         private final CursorListener listener;
610 
611         private Node componentNode;
612 
613         // Index of start of current component.
614         private int componentIndex;
615 
616         // Cursor within current component.
617         private ByteArray.Cursor componentCursor;
618 
619 
620         public CursorImpl()
621         {
622             this( 0, null );
623         }
624 
625 
626         public CursorImpl( int index )
627         {
628             this( index, null );
629         }
630 
631 
632         public CursorImpl( CursorListener listener )
633         {
634             this( 0, listener );
635         }
636 
637 
638         public CursorImpl( int index, CursorListener listener )
639         {
640             this.index = index;
641             this.listener = listener;
642         }
643 
644 
645         /**
646          * @inheritDoc
647          */
648         public int getIndex()
649         {
650             return index;
651         }
652 
653 
654         /**
655          * @inheritDoc
656          */
657         public void setIndex( int index )
658         {
659             checkBounds( index, 0 );
660             this.index = index;
661         }
662 
663 
664         /**
665          * @inheritDoc
666          */
667         public void skip( int length )
668         {
669             setIndex( index + length );
670         }
671 
672 
673         /**
674          * @inheritDoc
675          */
676         public ByteArray slice( int length )
677         {
678             CompositeByteArray slice = new CompositeByteArray( byteArrayFactory );
679             int remaining = length;
680             while ( remaining > 0 )
681             {
682                 prepareForAccess( remaining );
683                 int componentSliceSize = Math.min( remaining, componentCursor.getRemaining() );
684                 ByteArray componentSlice = componentCursor.slice( componentSliceSize );
685                 slice.addLast( componentSlice );
686                 index += componentSliceSize;
687                 remaining -= componentSliceSize;
688             }
689             return slice;
690         }
691 
692 
693         /**
694          * @inheritDoc
695          */
696         public ByteOrder order()
697         {
698             return CompositeByteArray.this.order();
699         }
700 
701 
702         private void prepareForAccess( int accessSize )
703         {
704             // Handle removed node. Do this first so we can remove the reference
705             // even if bounds checking fails.
706             if ( componentNode != null && componentNode.isRemoved() )
707             {
708                 componentNode = null;
709                 componentCursor = null;
710             }
711 
712             // Bounds checks
713             checkBounds( index, accessSize );
714 
715             // Remember the current node so we can later tell whether or not we
716             // need to create a new cursor.
717             Node oldComponentNode = componentNode;
718 
719             // Handle missing node.
720             if ( componentNode == null )
721             {
722                 int basMidpoint = ( last() - first() ) / 2 + first();
723                 if ( index <= basMidpoint )
724                 {
725                     // Search from the start.
726                     componentNode = bas.getFirst();
727                     componentIndex = first();
728                     if ( listener != null )
729                     {
730                         listener.enteredFirstComponent( componentIndex, componentNode.getByteArray() );
731                     }
732                 }
733                 else
734                 {
735                     // Search from the end.
736                     componentNode = bas.getLast();
737                     componentIndex = last() - componentNode.getByteArray().last();
738                     if ( listener != null )
739                     {
740                         listener.enteredLastComponent( componentIndex, componentNode.getByteArray() );
741                     }
742                 }
743             }
744 
745             // Go back, if necessary.
746             while ( index < componentIndex )
747             {
748                 componentNode = componentNode.getPreviousNode();
749                 componentIndex -= componentNode.getByteArray().last();
750                 if ( listener != null )
751                 {
752                     listener.enteredPreviousComponent( componentIndex, componentNode.getByteArray() );
753                 }
754             }
755 
756             // Go forward, if necessary.
757             while ( index >= componentIndex + componentNode.getByteArray().length() )
758             {
759                 componentIndex += componentNode.getByteArray().last();
760                 componentNode = componentNode.getNextNode();
761                 if ( listener != null )
762                 {
763                     listener.enteredNextComponent( componentIndex, componentNode.getByteArray() );
764                 }
765             }
766 
767             // Update the cursor.
768             int internalComponentIndex = index - componentIndex;
769             if ( componentNode == oldComponentNode )
770             {
771                 // Move existing cursor.
772                 componentCursor.setIndex( internalComponentIndex );
773             }
774             else
775             {
776                 // Create new cursor.
777                 componentCursor = componentNode.getByteArray().cursor( internalComponentIndex );
778             }
779         }
780 
781 
782         /**
783          * @inheritDoc
784          */
785         public int getRemaining()
786         {
787             return last() - index + 1;
788         }
789 
790 
791         /**
792          * @inheritDoc
793          */
794         public boolean hasRemaining()
795         {
796             return getRemaining() > 0;
797         }
798 
799 
800         /**
801          * @inheritDoc
802          */
803         public byte get()
804         {
805             prepareForAccess( 1 );
806             byte b = componentCursor.get();
807             index += 1;
808             return b;
809         }
810 
811 
812         /**
813          * @inheritDoc
814          */
815         public void put( byte b )
816         {
817             prepareForAccess( 1 );
818             componentCursor.put( b );
819             index += 1;
820         }
821 
822 
823         /**
824          * @inheritDoc
825          */
826         public void get( IoBuffer bb )
827         {
828             while ( bb.hasRemaining() )
829             {
830                 int remainingBefore = bb.remaining();
831                 prepareForAccess( remainingBefore );
832                 componentCursor.get( bb );
833                 int remainingAfter = bb.remaining();
834                 // Advance index by actual amount got.
835                 int chunkSize = remainingBefore - remainingAfter;
836                 index += chunkSize;
837             }
838         }
839 
840 
841         /**
842          * @inheritDoc
843          */
844         public void put( IoBuffer bb )
845         {
846             while ( bb.hasRemaining() )
847             {
848                 int remainingBefore = bb.remaining();
849                 prepareForAccess( remainingBefore );
850                 componentCursor.put( bb );
851                 int remainingAfter = bb.remaining();
852                 // Advance index by actual amount put.
853                 int chunkSize = remainingBefore - remainingAfter;
854                 index += chunkSize;
855             }
856         }
857 
858 
859         /**
860          * @inheritDoc
861          */
862         public short getShort()
863         {
864             prepareForAccess( 2 );
865             if ( componentCursor.getRemaining() >= 4 )
866             {
867                 short s = componentCursor.getShort();
868                 index += 2;
869                 return s;
870             }
871             else
872             {
873                 byte b0 = get();
874                 byte b1 = get();
875                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
876                 {
877                     return ( short ) ( ( b0 << 8 ) | ( b1 << 0 ) );
878                 }
879                 else
880                 {
881                     return ( short ) ( ( b1 << 8 ) | ( b0 << 0 ) );
882                 }
883             }
884         }
885 
886 
887         /**
888          * @inheritDoc
889          */
890         public void putShort( short s )
891         {
892             prepareForAccess( 2 );
893             if ( componentCursor.getRemaining() >= 4 )
894             {
895                 componentCursor.putShort( s );
896                 index += 2;
897             }
898             else
899             {
900                 byte b0;
901                 byte b1;
902                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
903                 {
904                     b0 = ( byte ) ( ( s >> 8 ) & 0xff );
905                     b1 = ( byte ) ( ( s >> 0 ) & 0xff );
906                 }
907                 else
908                 {
909                     b0 = ( byte ) ( ( s >> 0 ) & 0xff );
910                     b1 = ( byte ) ( ( s >> 8 ) & 0xff );
911                 }
912                 put( b0 );
913                 put( b1 );
914             }
915         }
916 
917 
918         /**
919          * @inheritDoc
920          */
921         public int getInt()
922         {
923             prepareForAccess( 4 );
924             if ( componentCursor.getRemaining() >= 4 )
925             {
926                 int i = componentCursor.getInt();
927                 index += 4;
928                 return i;
929             }
930             else
931             {
932                 byte b0 = get();
933                 byte b1 = get();
934                 byte b2 = get();
935                 byte b3 = get();
936                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
937                 {
938                     return ( ( b0 << 24 ) | ( b1 << 16 ) | ( b2 << 8 ) | ( b3 << 0 ) );
939                 }
940                 else
941                 {
942                     return ( ( b3 << 24 ) | ( b2 << 16 ) | ( b1 << 8 ) | ( b0 << 0 ) );
943                 }
944             }
945         }
946 
947 
948         /**
949          * @inheritDoc
950          */
951         public void putInt( int i )
952         {
953             prepareForAccess( 4 );
954             if ( componentCursor.getRemaining() >= 4 )
955             {
956                 componentCursor.putInt( i );
957                 index += 4;
958             }
959             else
960             {
961                 byte b0;
962                 byte b1;
963                 byte b2;
964                 byte b3;
965                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
966                 {
967                     b0 = ( byte ) ( ( i >> 24 ) & 0xff );
968                     b1 = ( byte ) ( ( i >> 16 ) & 0xff );
969                     b2 = ( byte ) ( ( i >> 8 ) & 0xff );
970                     b3 = ( byte ) ( ( i >> 0 ) & 0xff );
971                 }
972                 else
973                 {
974                     b0 = ( byte ) ( ( i >> 0 ) & 0xff );
975                     b1 = ( byte ) ( ( i >> 8 ) & 0xff );
976                     b2 = ( byte ) ( ( i >> 16 ) & 0xff );
977                     b3 = ( byte ) ( ( i >> 24 ) & 0xff );
978                 }
979                 put( b0 );
980                 put( b1 );
981                 put( b2 );
982                 put( b3 );
983             }
984         }
985 
986 
987         /**
988          * @inheritDoc
989          */
990         public long getLong()
991         {
992             prepareForAccess( 8 );
993             if ( componentCursor.getRemaining() >= 4 )
994             {
995                 long l = componentCursor.getLong();
996                 index += 8;
997                 return l;
998             }
999             else
1000             {
1001                 byte b0 = get();
1002                 byte b1 = get();
1003                 byte b2 = get();
1004                 byte b3 = get();
1005                 byte b4 = get();
1006                 byte b5 = get();
1007                 byte b6 = get();
1008                 byte b7 = get();
1009                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
1010                 {
1011                     return ( ( b0 & 0xffL ) << 56 ) | ( ( b1 & 0xffL ) << 48 ) | ( ( b2 & 0xffL ) << 40 )
1012                         | ( ( b3 & 0xffL ) << 32 ) | ( ( b4 & 0xffL ) << 24 ) | ( ( b5 & 0xffL ) << 16 )
1013                         | ( ( b6 & 0xffL ) << 8 ) | ( ( b7 & 0xffL ) << 0 );
1014                 }
1015                 else
1016                 {
1017                     return ( ( b7 & 0xffL ) << 56 ) | ( ( b6 & 0xffL ) << 48 ) | ( ( b5 & 0xffL ) << 40 )
1018                         | ( ( b4 & 0xffL ) << 32 ) | ( ( b3 & 0xffL ) << 24 ) | ( ( b2 & 0xffL ) << 16 )
1019                         | ( ( b1 & 0xffL ) << 8 ) | ( ( b0 & 0xffL ) << 0 );
1020                 }
1021             }
1022         }
1023 
1024 
1025         /**
1026          * @inheritDoc
1027          */
1028         public void putLong( long l )
1029         {
1030             //TODO: see if there is some optimizing that can be done here
1031             prepareForAccess( 8 );
1032             if ( componentCursor.getRemaining() >= 4 )
1033             {
1034                 componentCursor.putLong( l );
1035                 index += 8;
1036             }
1037             else
1038             {
1039                 byte b0;
1040                 byte b1;
1041                 byte b2;
1042                 byte b3;
1043                 byte b4;
1044                 byte b5;
1045                 byte b6;
1046                 byte b7;
1047                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
1048                 {
1049                     b0 = ( byte ) ( ( l >> 56 ) & 0xff );
1050                     b1 = ( byte ) ( ( l >> 48 ) & 0xff );
1051                     b2 = ( byte ) ( ( l >> 40 ) & 0xff );
1052                     b3 = ( byte ) ( ( l >> 32 ) & 0xff );
1053                     b4 = ( byte ) ( ( l >> 24 ) & 0xff );
1054                     b5 = ( byte ) ( ( l >> 16 ) & 0xff );
1055                     b6 = ( byte ) ( ( l >> 8 ) & 0xff );
1056                     b7 = ( byte ) ( ( l >> 0 ) & 0xff );
1057                 }
1058                 else
1059                 {
1060                     b0 = ( byte ) ( ( l >> 0 ) & 0xff );
1061                     b1 = ( byte ) ( ( l >> 8 ) & 0xff );
1062                     b2 = ( byte ) ( ( l >> 16 ) & 0xff );
1063                     b3 = ( byte ) ( ( l >> 24 ) & 0xff );
1064                     b4 = ( byte ) ( ( l >> 32 ) & 0xff );
1065                     b5 = ( byte ) ( ( l >> 40 ) & 0xff );
1066                     b6 = ( byte ) ( ( l >> 48 ) & 0xff );
1067                     b7 = ( byte ) ( ( l >> 56 ) & 0xff );
1068                 }
1069                 put( b0 );
1070                 put( b1 );
1071                 put( b2 );
1072                 put( b3 );
1073                 put( b4 );
1074                 put( b5 );
1075                 put( b6 );
1076                 put( b7 );
1077             }
1078         }
1079 
1080 
1081         /**
1082          * @inheritDoc
1083          */
1084         public float getFloat()
1085         {
1086             prepareForAccess( 4 );
1087             if ( componentCursor.getRemaining() >= 4 )
1088             {
1089                 float f = componentCursor.getFloat();
1090                 index += 4;
1091                 return f;
1092             }
1093             else
1094             {
1095                 int i = getInt();
1096                 return Float.intBitsToFloat( i );
1097             }
1098         }
1099 
1100 
1101         /**
1102          * @inheritDoc
1103          */
1104         public void putFloat( float f )
1105         {
1106             prepareForAccess( 4 );
1107             if ( componentCursor.getRemaining() >= 4 )
1108             {
1109                 componentCursor.putFloat( f );
1110                 index += 4;
1111             }
1112             else
1113             {
1114                 int i = Float.floatToIntBits( f );
1115                 putInt( i );
1116             }
1117         }
1118 
1119 
1120         /**
1121          * @inheritDoc
1122          */
1123         public double getDouble()
1124         {
1125             prepareForAccess( 8 );
1126             if ( componentCursor.getRemaining() >= 4 )
1127             {
1128                 double d = componentCursor.getDouble();
1129                 index += 8;
1130                 return d;
1131             }
1132             else
1133             {
1134                 long l = getLong();
1135                 return Double.longBitsToDouble( l );
1136             }
1137         }
1138 
1139 
1140         /**
1141          * @inheritDoc
1142          */
1143         public void putDouble( double d )
1144         {
1145             prepareForAccess( 8 );
1146             if ( componentCursor.getRemaining() >= 4 )
1147             {
1148                 componentCursor.putDouble( d );
1149                 index += 8;
1150             }
1151             else
1152             {
1153                 long l = Double.doubleToLongBits( d );
1154                 putLong( l );
1155             }
1156         }
1157 
1158 
1159         /**
1160          * @inheritDoc
1161          */
1162         public char getChar()
1163         {
1164             prepareForAccess( 2 );
1165             if ( componentCursor.getRemaining() >= 4 )
1166             {
1167                 char c = componentCursor.getChar();
1168                 index += 2;
1169                 return c;
1170             }
1171             else
1172             {
1173                 byte b0 = get();
1174                 byte b1 = get();
1175                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
1176                 {
1177                     return ( char ) ( ( b0 << 8 ) | ( b1 << 0 ) );
1178                 }
1179                 else
1180                 {
1181                     return ( char ) ( ( b1 << 8 ) | ( b0 << 0 ) );
1182                 }
1183             }
1184         }
1185 
1186 
1187         /**
1188          * @inheritDoc
1189          */
1190         public void putChar( char c )
1191         {
1192             prepareForAccess( 2 );
1193             if ( componentCursor.getRemaining() >= 4 )
1194             {
1195                 componentCursor.putChar( c );
1196                 index += 2;
1197             }
1198             else
1199             {
1200                 byte b0;
1201                 byte b1;
1202                 if ( order.equals( ByteOrder.BIG_ENDIAN ) )
1203                 {
1204                     b0 = ( byte ) ( ( c >> 8 ) & 0xff );
1205                     b1 = ( byte ) ( ( c >> 0 ) & 0xff );
1206                 }
1207                 else
1208                 {
1209                     b0 = ( byte ) ( ( c >> 0 ) & 0xff );
1210                     b1 = ( byte ) ( ( c >> 8 ) & 0xff );
1211                 }
1212                 put( b0 );
1213                 put( b1 );
1214             }
1215         }
1216 
1217     }
1218 }