001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.util.byteaccess;
021
022import java.nio.ByteOrder;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026
027import org.apache.mina.core.buffer.IoBuffer;
028import org.apache.mina.util.byteaccess.ByteArrayList.Node;
029
030/**
031 * A ByteArray composed of other ByteArrays. Optimised for fast relative access
032 * via cursors. Absolute access methods are provided, but may perform poorly.
033 *
034 * TODO: Write about laziness of cursor implementation - how movement doesn't
035 * happen until actual get/put.
036 * 
037 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
038 */
039public final class CompositeByteArray extends AbstractByteArray {
040
041    /**
042     * Allows for efficient detection of component boundaries when using a cursor.
043     *
044     * TODO: Is this interface right?
045     */
046    public interface CursorListener {
047        /**
048         * Called when the first component in the composite is entered by the cursor.
049         * 
050         * @param componentIndex The component position
051         * @param component The component to use
052         */
053        void enteredFirstComponent(int componentIndex, ByteArray component);
054
055        /**
056         * Called when the next component in the composite is entered by the cursor.
057         * 
058         * @param componentIndex The component position
059         * @param component The component to use
060         */
061        void enteredNextComponent(int componentIndex, ByteArray component);
062
063        /**
064         * Called when the previous component in the composite is entered by the cursor.
065         * 
066         * @param componentIndex The component position
067         * @param component The component to use
068         */
069        void enteredPreviousComponent(int componentIndex, ByteArray component);
070
071        /**
072         * Called when the last component in the composite is entered by the cursor.
073         * 
074         * @param componentIndex The component position
075         * @param component The component to use
076         */
077        void enteredLastComponent(int componentIndex, ByteArray component);
078    }
079
080    /**
081     * Stores the underlying <code>ByteArray</code>s.
082     */
083    private final ByteArrayList bas = new ByteArrayList();
084
085    /**
086     * The byte order for data in the buffer
087     */
088    private ByteOrder order;
089
090    /**
091     * May be used in <code>getSingleIoBuffer</code>. Optional.
092     */
093    private final ByteArrayFactory byteArrayFactory;
094
095    /**
096     * Creates a new instance of CompositeByteArray.
097     */
098    public CompositeByteArray() {
099        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}