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. Optimised 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 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
129      *  The ByteArray to add to the list
130      */
131     public void addFirst(ByteArray ba) {
132         addHook(ba);
133         bas.addFirst(ba);
134     }
135 
136     /**
137      * Remove the first {@link ByteArray} in the list
138      *
139      * @return
140      *  The first ByteArray in the list
141      */
142     public ByteArray removeFirst() {
143         Node node = bas.removeFirst();
144         return node == null ? null : node.getByteArray();
145     }
146 
147     /**
148      * Remove component <code>ByteArray</code>s to the given index (splitting
149      * them if necessary) and returning them in a single <code>ByteArray</code>.
150      * The caller is responsible for freeing the returned object.
151      *
152      * TODO: Document free behaviour more thoroughly.
153      * 
154      * @param index The index from where we will remove bytes
155      * @return$ The resulting byte aaay
156      */
157     public ByteArray removeTo(int index) {
158         if (index < first() || index > last()) {
159             throw new IndexOutOfBoundsException();
160         }
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         
242         return node == null ? null : node.getByteArray();
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     private void checkBounds(int index, int accessSize) {
257         int lower = index;
258         int upper = index + accessSize;
259 
260         if (lower < first()) {
261             throw new IndexOutOfBoundsException("Index " + lower + " less than start " + first() + ".");
262         }
263 
264         if (upper > last()) {
265             throw new IndexOutOfBoundsException("Index " + upper + " greater than length " + last() + ".");
266         }
267     }
268 
269     /**
270      * {@inheritDoc}
271      */
272     public Iterable<IoBuffer> getIoBuffers() {
273         if (bas.isEmpty()) {
274             return Collections.emptyList();
275         }
276 
277         Collection<IoBuffer> result = new ArrayList<IoBuffer>();
278         Node node = bas.getFirst();
279 
280         for (IoBuffer bb : node.getByteArray().getIoBuffers()) {
281             result.add(bb);
282         }
283 
284         while (node.hasNextNode()) {
285             node = node.getNextNode();
286 
287             for (IoBuffer bb : node.getByteArray().getIoBuffers()) {
288                 result.add(bb);
289             }
290         }
291 
292         return result;
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     public IoBuffer getSingleIoBuffer() {
299         if (byteArrayFactory == null) {
300             throw new IllegalStateException(
301                     "Can't get single buffer from CompositeByteArray unless it has a ByteArrayFactory.");
302         }
303 
304         if (bas.isEmpty()) {
305             ByteArray ba = byteArrayFactory.create(1);
306             return ba.getSingleIoBuffer();
307         }
308 
309         int actualLength = last() - first();
310 
311         {
312             Node node = bas.getFirst();
313             ByteArray ba = node.getByteArray();
314 
315             if (ba.last() == actualLength) {
316                 return ba.getSingleIoBuffer();
317             }
318         }
319 
320         // Replace all nodes with a single node.
321         ByteArray target = byteArrayFactory.create(actualLength);
322         IoBuffer bb = target.getSingleIoBuffer();
323         Cursor cursor = cursor();
324         cursor.put(bb); // Copy all existing data into target IoBuffer.
325 
326         while (!bas.isEmpty()) {
327             Node node = bas.getLast();
328             ByteArray component = node.getByteArray();
329             bas.removeLast();
330             component.free();
331         }
332 
333         bas.addLast(target);
334         return bb;
335     }
336 
337     /**
338      * {@inheritDoc}
339      */
340     public Cursor cursor() {
341         return new CursorImpl();
342     }
343 
344     /**
345      * {@inheritDoc}
346      */
347     public Cursor cursor(int index) {
348         return new CursorImpl(index);
349     }
350 
351     /**
352      * Get a cursor starting at index 0 (which may not be the start of the
353      * array) and with the given listener.
354      * 
355      * @param listener The listener to use
356      * @return a new {@link ByteArray.Cursor} instance
357      */
358     public Cursor cursor(CursorListener listener) {
359         return new CursorImpl(listener);
360     }
361 
362     /**
363      * Get a cursor starting at the given index and with the given listener.
364      * 
365      * @param index The position of the array to start the Cursor at
366      * @param listener The listener for the Cursor that is returned
367      * @return The created Cursor
368      */
369     public Cursor cursor(int index, CursorListener listener) {
370         return new CursorImpl(index, listener);
371     }
372 
373     /**
374      * {@inheritDoc}
375      */
376     public ByteArray slice(int index, int length) {
377         return cursor(index).slice(length);
378     }
379 
380     /**
381      * {@inheritDoc}
382      */
383     public byte get(int index) {
384         return cursor(index).get();
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     public void put(int index, byte b) {
391         cursor(index).put(b);
392     }
393 
394     /**
395      * {@inheritDoc}
396      */
397     public void get(int index, IoBuffer bb) {
398         cursor(index).get(bb);
399     }
400 
401     /**
402      * {@inheritDoc}
403      */
404     public void put(int index, IoBuffer bb) {
405         cursor(index).put(bb);
406     }
407 
408     /**
409      * {@inheritDoc}
410      */
411     public int first() {
412         return bas.firstByte();
413     }
414 
415     /**
416      * {@inheritDoc}
417      */
418     public int last() {
419         return bas.lastByte();
420     }
421 
422     /**
423      * This method should be called prior to adding any component
424      * <code>ByteArray</code> to a composite.
425      *
426      * @param ba
427      *  The component to add.
428      */
429     private void addHook(ByteArray ba) {
430         // Check first() is zero, otherwise cursor might not work.
431         // TODO: Remove this restriction?
432         if (ba.first() != 0) {
433             throw new IllegalArgumentException("Cannot add byte array that doesn't start from 0: " + ba.first());
434         }
435         // Check order.
436         if (order == null) {
437             order = ba.order();
438         } else if (!order.equals(ba.order())) {
439             throw new IllegalArgumentException("Cannot add byte array with different byte order: " + ba.order());
440         }
441     }
442 
443     /**
444      * {@inheritDoc}
445      */
446     public ByteOrder order() {
447         if (order == null) {
448             throw new IllegalStateException("Byte order not yet set.");
449         }
450         return order;
451     }
452 
453     /**
454      * {@inheritDoc}
455      */
456     public void order(ByteOrder order) {
457         if (order == null || !order.equals(this.order)) {
458             this.order = order;
459 
460             if (!bas.isEmpty()) {
461                 for (Node node = bas.getFirst(); node.hasNextNode(); node = node.getNextNode()) {
462                     node.getByteArray().order(order);
463                 }
464             }
465         }
466     }
467 
468     /**
469      * {@inheritDoc}
470      */
471     public short getShort(int index) {
472         return cursor(index).getShort();
473     }
474 
475     /**
476      * {@inheritDoc}
477      */
478     public void putShort(int index, short s) {
479         cursor(index).putShort(s);
480     }
481 
482     /**
483      * {@inheritDoc}
484      */
485     public int getInt(int index) {
486         return cursor(index).getInt();
487     }
488 
489     /**
490      * {@inheritDoc}
491      */
492     public void putInt(int index, int i) {
493         cursor(index).putInt(i);
494     }
495 
496     /**
497      * {@inheritDoc}
498      */
499     public long getLong(int index) {
500         return cursor(index).getLong();
501     }
502 
503     /**
504      * {@inheritDoc}
505      */
506     public void putLong(int index, long l) {
507         cursor(index).putLong(l);
508     }
509 
510     /**
511      * {@inheritDoc}
512      */
513     public float getFloat(int index) {
514         return cursor(index).getFloat();
515     }
516 
517     /**
518      * {@inheritDoc}
519      */
520     public void putFloat(int index, float f) {
521         cursor(index).putFloat(f);
522     }
523 
524     /**
525      * {@inheritDoc}
526      */
527     public double getDouble(int index) {
528         return cursor(index).getDouble();
529     }
530 
531     /**
532      * {@inheritDoc}
533      */
534     public void putDouble(int index, double d) {
535         cursor(index).putDouble(d);
536     }
537 
538     /**
539      * {@inheritDoc}
540      */
541     public char getChar(int index) {
542         return cursor(index).getChar();
543     }
544 
545     /**
546      * {@inheritDoc}
547      */
548     public void putChar(int index, char c) {
549         cursor(index).putChar(c);
550     }
551 
552     private class CursorImpl implements Cursor {
553 
554         private int index;
555 
556         private final CursorListener listener;
557 
558         private Node componentNode;
559 
560         // Index of start of current component.
561         private int componentIndex;
562 
563         // Cursor within current component.
564         private ByteArray.Cursor componentCursor;
565 
566         public CursorImpl() {
567             this(0, null);
568         }
569 
570         public CursorImpl(int index) {
571             this(index, null);
572         }
573 
574         public CursorImpl(CursorListener listener) {
575             this(0, listener);
576         }
577 
578         public CursorImpl(int index, CursorListener listener) {
579             this.index = index;
580             this.listener = listener;
581         }
582 
583         /**
584          * {@inheritDoc}
585          */
586         public int getIndex() {
587             return index;
588         }
589 
590         /**
591          * {@inheritDoc}
592          */
593         public void setIndex(int index) {
594             checkBounds(index, 0);
595             this.index = index;
596         }
597 
598         /**
599          * {@inheritDoc}
600          */
601         public void skip(int length) {
602             setIndex(index + length);
603         }
604 
605         /**
606          * {@inheritDoc}
607          */
608         public ByteArray slice(int length) {
609             CompositeByteArray slice = new CompositeByteArray(byteArrayFactory);
610             int remaining = length;
611             while (remaining > 0) {
612                 prepareForAccess(remaining);
613                 int componentSliceSize = Math.min(remaining, componentCursor.getRemaining());
614                 ByteArray componentSlice = componentCursor.slice(componentSliceSize);
615                 slice.addLast(componentSlice);
616                 index += componentSliceSize;
617                 remaining -= componentSliceSize;
618             }
619             return slice;
620         }
621 
622         /**
623          * {@inheritDoc}
624          */
625         public ByteOrder order() {
626             return CompositeByteArray.this.order();
627         }
628 
629         private void prepareForAccess(int accessSize) {
630             // Handle removed node. Do this first so we can remove the reference
631             // even if bounds checking fails.
632             if (componentNode != null && componentNode.isRemoved()) {
633                 componentNode = null;
634                 componentCursor = null;
635             }
636 
637             // Bounds checks
638             checkBounds(index, accessSize);
639 
640             // Remember the current node so we can later tell whether or not we
641             // need to create a new cursor.
642             Node oldComponentNode = componentNode;
643 
644             // Handle missing node.
645             if (componentNode == null) {
646                 int basMidpoint = (last() - first()) / 2 + first();
647                 if (index <= basMidpoint) {
648                     // Search from the start.
649                     componentNode = bas.getFirst();
650                     componentIndex = first();
651                     if (listener != null) {
652                         listener.enteredFirstComponent(componentIndex, componentNode.getByteArray());
653                     }
654                 } else {
655                     // Search from the end.
656                     componentNode = bas.getLast();
657                     componentIndex = last() - componentNode.getByteArray().last();
658                     if (listener != null) {
659                         listener.enteredLastComponent(componentIndex, componentNode.getByteArray());
660                     }
661                 }
662             }
663 
664             // Go back, if necessary.
665             while (index < componentIndex) {
666                 componentNode = componentNode.getPreviousNode();
667                 componentIndex -= componentNode.getByteArray().last();
668                 if (listener != null) {
669                     listener.enteredPreviousComponent(componentIndex, componentNode.getByteArray());
670                 }
671             }
672 
673             // Go forward, if necessary.
674             while (index >= componentIndex + componentNode.getByteArray().length()) {
675                 componentIndex += componentNode.getByteArray().last();
676                 componentNode = componentNode.getNextNode();
677                 if (listener != null) {
678                     listener.enteredNextComponent(componentIndex, componentNode.getByteArray());
679                 }
680             }
681 
682             // Update the cursor.
683             int internalComponentIndex = index - componentIndex;
684             if (componentNode == oldComponentNode) {
685                 // Move existing cursor.
686                 componentCursor.setIndex(internalComponentIndex);
687             } else {
688                 // Create new cursor.
689                 componentCursor = componentNode.getByteArray().cursor(internalComponentIndex);
690             }
691         }
692 
693         /**
694          * {@inheritDoc}
695          */
696         public int getRemaining() {
697             return last() - index + 1;
698         }
699 
700         /**
701          * {@inheritDoc}
702          */
703         public boolean hasRemaining() {
704             return getRemaining() > 0;
705         }
706 
707         /**
708          * {@inheritDoc}
709          */
710         public byte get() {
711             prepareForAccess(1);
712             byte b = componentCursor.get();
713             index += 1;
714             return b;
715         }
716 
717         /**
718          * {@inheritDoc}
719          */
720         public void put(byte b) {
721             prepareForAccess(1);
722             componentCursor.put(b);
723             index += 1;
724         }
725 
726         /**
727          * {@inheritDoc}
728          */
729         public void get(IoBuffer bb) {
730             while (bb.hasRemaining()) {
731                 int remainingBefore = bb.remaining();
732                 prepareForAccess(remainingBefore);
733                 componentCursor.get(bb);
734                 int remainingAfter = bb.remaining();
735                 // Advance index by actual amount got.
736                 int chunkSize = remainingBefore - remainingAfter;
737                 index += chunkSize;
738             }
739         }
740 
741         /**
742          * {@inheritDoc}
743          */
744         public void put(IoBuffer bb) {
745             while (bb.hasRemaining()) {
746                 int remainingBefore = bb.remaining();
747                 prepareForAccess(remainingBefore);
748                 componentCursor.put(bb);
749                 int remainingAfter = bb.remaining();
750                 // Advance index by actual amount put.
751                 int chunkSize = remainingBefore - remainingAfter;
752                 index += chunkSize;
753             }
754         }
755 
756         /**
757          * {@inheritDoc}
758          */
759         public short getShort() {
760             prepareForAccess(2);
761             if (componentCursor.getRemaining() >= 4) {
762                 short s = componentCursor.getShort();
763                 index += 2;
764                 return s;
765             } else {
766                 byte b0 = get();
767                 byte b1 = get();
768                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
769                     return (short) ((b0 << 8) | (b1 & 0xFF));
770                 } else {
771                     return (short) ((b1 << 8) | (b0 & 0xFF));
772                 }
773             }
774         }
775 
776         /**
777          * {@inheritDoc}
778          */
779         public void putShort(short s) {
780             prepareForAccess(2);
781             if (componentCursor.getRemaining() >= 4) {
782                 componentCursor.putShort(s);
783                 index += 2;
784             } else {
785                 byte b0;
786                 byte b1;
787                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
788                     b0 = (byte) ((s >> 8) & 0xff);
789                     b1 = (byte) ((s >> 0) & 0xff);
790                 } else {
791                     b0 = (byte) ((s >> 0) & 0xff);
792                     b1 = (byte) ((s >> 8) & 0xff);
793                 }
794                 put(b0);
795                 put(b1);
796             }
797         }
798 
799         /**
800          * {@inheritDoc}
801          */
802         public int getInt() {
803             prepareForAccess(4);
804             if (componentCursor.getRemaining() >= 4) {
805                 int i = componentCursor.getInt();
806                 index += 4;
807                 return i;
808             } else {
809                 byte b0 = get();
810                 byte b1 = get();
811                 byte b2 = get();
812                 byte b3 = get();
813                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
814                     return (b0 << 24) | ((b1 & 0xFF) << 16) | ((b2 & 0xFF) << 8) | (b3 & 0xFF);
815                 } else {
816                     return (b3 << 24) | ((b2 & 0xFF) << 16) | ((b1 & 0xFF) << 8) | (b0 & 0xFF);
817                 }
818             }
819         }
820 
821         /**
822          * {@inheritDoc}
823          */
824         public void putInt(int i) {
825             prepareForAccess(4);
826             if (componentCursor.getRemaining() >= 4) {
827                 componentCursor.putInt(i);
828                 index += 4;
829             } else {
830                 byte b0;
831                 byte b1;
832                 byte b2;
833                 byte b3;
834                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
835                     b0 = (byte) ((i >> 24) & 0xff);
836                     b1 = (byte) ((i >> 16) & 0xff);
837                     b2 = (byte) ((i >> 8) & 0xff);
838                     b3 = (byte) ((i >> 0) & 0xff);
839                 } else {
840                     b0 = (byte) ((i >> 0) & 0xff);
841                     b1 = (byte) ((i >> 8) & 0xff);
842                     b2 = (byte) ((i >> 16) & 0xff);
843                     b3 = (byte) ((i >> 24) & 0xff);
844                 }
845                 put(b0);
846                 put(b1);
847                 put(b2);
848                 put(b3);
849             }
850         }
851 
852         /**
853          * {@inheritDoc}
854          */
855         public long getLong() {
856             prepareForAccess(8);
857             if (componentCursor.getRemaining() >= 4) {
858                 long l = componentCursor.getLong();
859                 index += 8;
860                 return l;
861             } else {
862                 byte b0 = get();
863                 byte b1 = get();
864                 byte b2 = get();
865                 byte b3 = get();
866                 byte b4 = get();
867                 byte b5 = get();
868                 byte b6 = get();
869                 byte b7 = get();
870                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
871                     return ((b0 & 0xFFL) << 56) | ((b1 & 0xFFL) << 48) | ((b2 & 0xFFL) << 40) | ((b3 & 0xFFL) << 32)
872                             | ((b4 & 0xFFL) << 24) | ((b5 & 0xFFL) << 16) | ((b6 & 0xFFL) << 8) | (b7 & 0xFFL);
873                 } else {
874                     return ((b7 & 0xFFL) << 56) | ((b6 & 0xFFL) << 48) | ((b5 & 0xFFL) << 40) | ((b4 & 0xFFL) << 32)
875                             | ((b3 & 0xFFL) << 24) | ((b2 & 0xFFL) << 16) | ((b1 & 0xFFL) << 8) | (b0 & 0xFFL);
876                 }
877             }
878         }
879 
880         /**
881          * {@inheritDoc}
882          */
883         public void putLong(long l) {
884             //TODO: see if there is some optimizing that can be done here
885             prepareForAccess(8);
886             if (componentCursor.getRemaining() >= 4) {
887                 componentCursor.putLong(l);
888                 index += 8;
889             } else {
890                 byte b0;
891                 byte b1;
892                 byte b2;
893                 byte b3;
894                 byte b4;
895                 byte b5;
896                 byte b6;
897                 byte b7;
898                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
899                     b0 = (byte) ((l >> 56) & 0xff);
900                     b1 = (byte) ((l >> 48) & 0xff);
901                     b2 = (byte) ((l >> 40) & 0xff);
902                     b3 = (byte) ((l >> 32) & 0xff);
903                     b4 = (byte) ((l >> 24) & 0xff);
904                     b5 = (byte) ((l >> 16) & 0xff);
905                     b6 = (byte) ((l >> 8) & 0xff);
906                     b7 = (byte) ((l >> 0) & 0xff);
907                 } else {
908                     b0 = (byte) ((l >> 0) & 0xff);
909                     b1 = (byte) ((l >> 8) & 0xff);
910                     b2 = (byte) ((l >> 16) & 0xff);
911                     b3 = (byte) ((l >> 24) & 0xff);
912                     b4 = (byte) ((l >> 32) & 0xff);
913                     b5 = (byte) ((l >> 40) & 0xff);
914                     b6 = (byte) ((l >> 48) & 0xff);
915                     b7 = (byte) ((l >> 56) & 0xff);
916                 }
917                 put(b0);
918                 put(b1);
919                 put(b2);
920                 put(b3);
921                 put(b4);
922                 put(b5);
923                 put(b6);
924                 put(b7);
925             }
926         }
927 
928         /**
929          * {@inheritDoc}
930          */
931         public float getFloat() {
932             prepareForAccess(4);
933             if (componentCursor.getRemaining() >= 4) {
934                 float f = componentCursor.getFloat();
935                 index += 4;
936                 return f;
937             } else {
938                 int i = getInt();
939                 return Float.intBitsToFloat(i);
940             }
941         }
942 
943         /**
944          * {@inheritDoc}
945          */
946         public void putFloat(float f) {
947             prepareForAccess(4);
948             if (componentCursor.getRemaining() >= 4) {
949                 componentCursor.putFloat(f);
950                 index += 4;
951             } else {
952                 int i = Float.floatToIntBits(f);
953                 putInt(i);
954             }
955         }
956 
957         /**
958          * {@inheritDoc}
959          */
960         public double getDouble() {
961             prepareForAccess(8);
962             if (componentCursor.getRemaining() >= 4) {
963                 double d = componentCursor.getDouble();
964                 index += 8;
965                 return d;
966             } else {
967                 long l = getLong();
968                 return Double.longBitsToDouble(l);
969             }
970         }
971 
972         /**
973          * {@inheritDoc}
974          */
975         public void putDouble(double d) {
976             prepareForAccess(8);
977             if (componentCursor.getRemaining() >= 4) {
978                 componentCursor.putDouble(d);
979                 index += 8;
980             } else {
981                 long l = Double.doubleToLongBits(d);
982                 putLong(l);
983             }
984         }
985 
986         /**
987          * {@inheritDoc}
988          */
989         public char getChar() {
990             prepareForAccess(2);
991             if (componentCursor.getRemaining() >= 4) {
992                 char c = componentCursor.getChar();
993                 index += 2;
994                 return c;
995             } else {
996                 byte b0 = get();
997                 byte b1 = get();
998                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
999                     return (char)((b0 << 8) | (b1 & 0xFF));
1000                 } else {
1001                     return (char)((b1 << 8) | (b0 & 0xFF));
1002                 }
1003             }
1004         }
1005 
1006         /**
1007          * {@inheritDoc}
1008          */
1009         public void putChar(char c) {
1010             prepareForAccess(2);
1011             if (componentCursor.getRemaining() >= 4) {
1012                 componentCursor.putChar(c);
1013                 index += 2;
1014             } else {
1015                 byte b0;
1016                 byte b1;
1017                 if (order.equals(ByteOrder.BIG_ENDIAN)) {
1018                     b0 = (byte) ((c >> 8) & 0xff);
1019                     b1 = (byte) ((c >> 0) & 0xff);
1020                 } else {
1021                     b0 = (byte) ((c >> 0) & 0xff);
1022                     b1 = (byte) ((c >> 8) & 0xff);
1023                 }
1024                 put(b0);
1025                 put(b1);
1026             }
1027         }
1028 
1029     }
1030 }