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