View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.buffer;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.OutputStream;
21  import java.nio.ByteBuffer;
22  import java.nio.ByteOrder;
23  import java.nio.channels.GatheringByteChannel;
24  import java.nio.channels.ScatteringByteChannel;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import org.jboss.netty.util.internal.DetectionUtil;
30  
31  /**
32   * A virtual buffer which shows multiple buffers as a single merged buffer.  It
33   * is recommended to use {@link ChannelBuffers#wrappedBuffer(ChannelBuffer...)}
34   * instead of calling the constructor explicitly.
35   */
36  public class CompositeChannelBuffer extends AbstractChannelBuffer {
37  
38      private final ByteOrder order;
39      private ChannelBuffer[] components;
40      private int[] indices;
41      private int lastAccessedComponentId;
42      private final boolean gathering;
43  
44      public CompositeChannelBuffer(ByteOrder endianness, List<ChannelBuffer> buffers, boolean gathering) {
45          order = endianness;
46          this.gathering = gathering;
47          setComponents(buffers);
48      }
49  
50      /**
51       * Return <code>true</code> if gathering writes / reads should be used
52       * for this {@link CompositeChannelBuffer}
53       */
54      public boolean useGathering() {
55          return gathering && DetectionUtil.javaVersion() >= 7;
56      }
57  
58      /**
59       * Same with {@link #slice(int, int)} except that this method returns a list.
60       */
61      public List<ChannelBuffer> decompose(int index, int length) {
62          if (length == 0) {
63              return Collections.emptyList();
64          }
65  
66          if (index + length > capacity()) {
67              throw new IndexOutOfBoundsException("Too many bytes to decompose - Need "
68                      + (index + length) + ", capacity is " + capacity());
69          }
70  
71          int componentId = componentId(index);
72          List<ChannelBuffer> slice = new ArrayList<ChannelBuffer>(components.length);
73  
74          // The first component
75          ChannelBuffer first = components[componentId].duplicate();
76          first.readerIndex(index - indices[componentId]);
77  
78          ChannelBuffer buf = first;
79          int bytesToSlice = length;
80          do {
81              int readableBytes = buf.readableBytes();
82              if (bytesToSlice <= readableBytes) {
83                  // Last component
84                  buf.writerIndex(buf.readerIndex() + bytesToSlice);
85                  slice.add(buf);
86                  break;
87              } else {
88                  // Not the last component
89                  slice.add(buf);
90                  bytesToSlice -= readableBytes;
91                  componentId ++;
92  
93                  // Fetch the next component.
94                  buf = components[componentId].duplicate();
95              }
96          } while (bytesToSlice > 0);
97  
98          // Slice all components because only readable bytes are interesting.
99          for (int i = 0; i < slice.size(); i ++) {
100             slice.set(i, slice.get(i).slice());
101         }
102 
103         return slice;
104     }
105 
106     /**
107      * Setup this ChannelBuffer from the list
108      */
109     private void setComponents(List<ChannelBuffer> newComponents) {
110         assert !newComponents.isEmpty();
111 
112         // Clear the cache.
113         lastAccessedComponentId = 0;
114 
115         // Build the component array.
116         components = new ChannelBuffer[newComponents.size()];
117         for (int i = 0; i < components.length; i ++) {
118             ChannelBuffer c = newComponents.get(i);
119             if (c.order() != order()) {
120                 throw new IllegalArgumentException(
121                         "All buffers must have the same endianness.");
122             }
123 
124             assert c.readerIndex() == 0;
125             assert c.writerIndex() == c.capacity();
126 
127             components[i] = c;
128         }
129 
130         // Build the component lookup table.
131         indices = new int[components.length + 1];
132         indices[0] = 0;
133         for (int i = 1; i <= components.length; i ++) {
134             indices[i] = indices[i - 1] + components[i - 1].capacity();
135         }
136 
137         // Reset the indexes.
138         setIndex(0, capacity());
139     }
140 
141     private CompositeChannelBuffer(CompositeChannelBuffer buffer) {
142         order = buffer.order;
143         gathering = buffer.gathering;
144         components = buffer.components.clone();
145         indices = buffer.indices.clone();
146         setIndex(buffer.readerIndex(), buffer.writerIndex());
147     }
148 
149     public ChannelBufferFactory factory() {
150         return HeapChannelBufferFactory.getInstance(order());
151     }
152 
153     public ByteOrder order() {
154         return order;
155     }
156 
157     public boolean isDirect() {
158         return false;
159     }
160 
161     public boolean hasArray() {
162         return false;
163     }
164 
165     public byte[] array() {
166         throw new UnsupportedOperationException();
167     }
168 
169     public int arrayOffset() {
170         throw new UnsupportedOperationException();
171     }
172 
173     public int capacity() {
174         return indices[components.length];
175     }
176 
177     public int numComponents() {
178         return components.length;
179     }
180 
181     public byte getByte(int index) {
182         int componentId = componentId(index);
183         return components[componentId].getByte(index - indices[componentId]);
184     }
185 
186     public short getShort(int index) {
187         int componentId = componentId(index);
188         if (index + 2 <= indices[componentId + 1]) {
189             return components[componentId].getShort(index - indices[componentId]);
190         } else if (order() == ByteOrder.BIG_ENDIAN) {
191             return (short) ((getByte(index) & 0xff) << 8 | getByte(index + 1) & 0xff);
192         } else {
193             return (short) (getByte(index) & 0xff | (getByte(index + 1) & 0xff) << 8);
194         }
195     }
196 
197     public int getUnsignedMedium(int index) {
198         int componentId = componentId(index);
199         if (index + 3 <= indices[componentId + 1]) {
200             return components[componentId].getUnsignedMedium(index - indices[componentId]);
201         } else if (order() == ByteOrder.BIG_ENDIAN) {
202             return (getShort(index) & 0xffff) << 8 | getByte(index + 2) & 0xff;
203         } else {
204             return getShort(index) & 0xFFFF | (getByte(index + 2) & 0xFF) << 16;
205         }
206     }
207 
208     public int getInt(int index) {
209         int componentId = componentId(index);
210         if (index + 4 <= indices[componentId + 1]) {
211             return components[componentId].getInt(index - indices[componentId]);
212         } else if (order() == ByteOrder.BIG_ENDIAN) {
213             return (getShort(index) & 0xffff) << 16 | getShort(index + 2) & 0xffff;
214         } else {
215             return getShort(index) & 0xFFFF | (getShort(index + 2) & 0xFFFF) << 16;
216         }
217     }
218 
219     public long getLong(int index) {
220         int componentId = componentId(index);
221         if (index + 8 <= indices[componentId + 1]) {
222             return components[componentId].getLong(index - indices[componentId]);
223         } else if (order() == ByteOrder.BIG_ENDIAN) {
224             return (getInt(index) & 0xffffffffL) << 32 | getInt(index + 4) & 0xffffffffL;
225         } else {
226             return getInt(index) & 0xFFFFFFFFL | (getInt(index + 4) & 0xFFFFFFFFL) << 32;
227         }
228     }
229 
230     public void getBytes(int index, byte[] dst, int dstIndex, int length) {
231         int componentId = componentId(index);
232         if (index > capacity() - length || dstIndex > dst.length - length) {
233             throw new IndexOutOfBoundsException("Too many bytes to read - Needs "
234                     + (index + length) + ", maximum is " + capacity() + " or "
235                     + dst.length);
236         }
237 
238         int i = componentId;
239         while (length > 0) {
240             ChannelBuffer s = components[i];
241             int adjustment = indices[i];
242             int localLength = Math.min(length, s.capacity() - (index - adjustment));
243             s.getBytes(index - adjustment, dst, dstIndex, localLength);
244             index += localLength;
245             dstIndex += localLength;
246             length -= localLength;
247             i ++;
248         }
249     }
250 
251     public void getBytes(int index, ByteBuffer dst) {
252         int componentId = componentId(index);
253         int limit = dst.limit();
254         int length = dst.remaining();
255         if (index > capacity() - length) {
256             throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
257                     + (index + length) + ", maximum is " + capacity());
258         }
259 
260         int i = componentId;
261         try {
262             while (length > 0) {
263                 ChannelBuffer s = components[i];
264                 int adjustment = indices[i];
265                 int localLength = Math.min(length, s.capacity() - (index - adjustment));
266                 dst.limit(dst.position() + localLength);
267                 s.getBytes(index - adjustment, dst);
268                 index += localLength;
269                 length -= localLength;
270                 i ++;
271             }
272         } finally {
273             dst.limit(limit);
274         }
275     }
276 
277     public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) {
278         int componentId = componentId(index);
279         if (index > capacity() - length || dstIndex > dst.capacity() - length) {
280             throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
281                     + (index + length) + " or " + (dstIndex + length) + ", maximum is "
282                     + capacity() + " or " + dst.capacity());
283         }
284 
285         int i = componentId;
286         while (length > 0) {
287             ChannelBuffer s = components[i];
288             int adjustment = indices[i];
289             int localLength = Math.min(length, s.capacity() - (index - adjustment));
290             s.getBytes(index - adjustment, dst, dstIndex, localLength);
291             index += localLength;
292             dstIndex += localLength;
293             length -= localLength;
294             i ++;
295         }
296     }
297 
298     public int getBytes(int index, GatheringByteChannel out, int length)
299             throws IOException {
300 
301         if (useGathering()) {
302             return (int) out.write(toByteBuffers(index, length));
303         }
304 
305         // XXX Gathering write is not supported because of a known issue.
306         //     See http://bugs.sun.com/view_bug.do?bug_id=6210541
307         //     This issue appeared in 2004 and is still unresolved!?
308         return out.write(toByteBuffer(index, length));
309     }
310 
311     public void getBytes(int index, OutputStream out, int length)
312             throws IOException {
313         int componentId = componentId(index);
314         if (index > capacity() - length) {
315             throw new IndexOutOfBoundsException("Too many bytes to be read - needs "
316                     + (index + length) + ", maximum of " + capacity());
317         }
318 
319         int i = componentId;
320         while (length > 0) {
321             ChannelBuffer s = components[i];
322             int adjustment = indices[i];
323             int localLength = Math.min(length, s.capacity() - (index - adjustment));
324             s.getBytes(index - adjustment, out, localLength);
325             index += localLength;
326             length -= localLength;
327             i ++;
328         }
329     }
330 
331     public void setByte(int index, int value) {
332         int componentId = componentId(index);
333         components[componentId].setByte(index - indices[componentId], value);
334     }
335 
336     public void setShort(int index, int value) {
337         int componentId = componentId(index);
338         if (index + 2 <= indices[componentId + 1]) {
339             components[componentId].setShort(index - indices[componentId], value);
340         } else if (order() == ByteOrder.BIG_ENDIAN) {
341             setByte(index, (byte) (value >>> 8));
342             setByte(index + 1, (byte) value);
343         } else {
344             setByte(index    , (byte) value);
345             setByte(index + 1, (byte) (value >>> 8));
346         }
347     }
348 
349     public void setMedium(int index, int value) {
350         int componentId = componentId(index);
351         if (index + 3 <= indices[componentId + 1]) {
352             components[componentId].setMedium(index - indices[componentId], value);
353         } else if (order() == ByteOrder.BIG_ENDIAN) {
354             setShort(index, (short) (value >> 8));
355             setByte(index + 2, (byte) value);
356         } else {
357             setShort(index   , (short) value);
358             setByte(index + 2, (byte) (value >>> 16));
359         }
360     }
361 
362     public void setInt(int index, int value) {
363         int componentId = componentId(index);
364         if (index + 4 <= indices[componentId + 1]) {
365             components[componentId].setInt(index - indices[componentId], value);
366         } else if (order() == ByteOrder.BIG_ENDIAN) {
367             setShort(index, (short) (value >>> 16));
368             setShort(index + 2, (short) value);
369         } else {
370             setShort(index    , (short) value);
371             setShort(index + 2, (short) (value >>> 16));
372         }
373     }
374 
375     public void setLong(int index, long value) {
376         int componentId = componentId(index);
377         if (index + 8 <= indices[componentId + 1]) {
378             components[componentId].setLong(index - indices[componentId], value);
379         } else if (order() == ByteOrder.BIG_ENDIAN) {
380             setInt(index, (int) (value >>> 32));
381             setInt(index + 4, (int) value);
382         } else {
383             setInt(index    , (int) value);
384             setInt(index + 4, (int) (value >>> 32));
385         }
386     }
387 
388     public void setBytes(int index, byte[] src, int srcIndex, int length) {
389         int componentId = componentId(index);
390         if (index > capacity() - length || srcIndex > src.length - length) {
391             throw new IndexOutOfBoundsException("Too many bytes to read - needs "
392                     + (index + length) + " or " + (srcIndex + length) + ", maximum is "
393                     + capacity() + " or " + src.length);
394         }
395 
396         int i = componentId;
397         while (length > 0) {
398             ChannelBuffer s = components[i];
399             int adjustment = indices[i];
400             int localLength = Math.min(length, s.capacity() - (index - adjustment));
401             s.setBytes(index - adjustment, src, srcIndex, localLength);
402             index += localLength;
403             srcIndex += localLength;
404             length -= localLength;
405             i ++;
406         }
407     }
408 
409     public void setBytes(int index, ByteBuffer src) {
410         int componentId = componentId(index);
411         int limit = src.limit();
412         int length = src.remaining();
413         if (index > capacity() - length) {
414             throw new IndexOutOfBoundsException("Too many bytes to be written - Needs "
415                     + (index + length) + ", maximum is " + capacity());
416         }
417 
418         int i = componentId;
419         try {
420             while (length > 0) {
421                 ChannelBuffer s = components[i];
422                 int adjustment = indices[i];
423                 int localLength = Math.min(length, s.capacity() - (index - adjustment));
424                 src.limit(src.position() + localLength);
425                 s.setBytes(index - adjustment, src);
426                 index += localLength;
427                 length -= localLength;
428                 i ++;
429             }
430         } finally {
431             src.limit(limit);
432         }
433     }
434 
435     public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) {
436         int componentId = componentId(index);
437         if (index > capacity() - length || srcIndex > src.capacity() - length) {
438             throw new IndexOutOfBoundsException("Too many bytes to be written - Needs "
439                     + (index + length) + " or " + (srcIndex + length) + ", maximum is "
440                     + capacity() + " or " + src.capacity());
441         }
442 
443         int i = componentId;
444         while (length > 0) {
445             ChannelBuffer s = components[i];
446             int adjustment = indices[i];
447             int localLength = Math.min(length, s.capacity() - (index - adjustment));
448             s.setBytes(index - adjustment, src, srcIndex, localLength);
449             index += localLength;
450             srcIndex += localLength;
451             length -= localLength;
452             i ++;
453         }
454     }
455 
456     public int setBytes(int index, InputStream in, int length)
457             throws IOException {
458         int componentId = componentId(index);
459         if (index > capacity() - length) {
460             throw new IndexOutOfBoundsException("Too many bytes to write - Needs "
461                     + (index + length) + ", maximum is " + capacity());
462         }
463 
464         int i = componentId;
465         int readBytes = 0;
466 
467         do {
468             ChannelBuffer s = components[i];
469             int adjustment = indices[i];
470             int localLength = Math.min(length, s.capacity() - (index - adjustment));
471             int localReadBytes = s.setBytes(index - adjustment, in, localLength);
472             if (localReadBytes < 0) {
473                 if (readBytes == 0) {
474                     return -1;
475                 } else {
476                     break;
477                 }
478             }
479 
480             if (localReadBytes == localLength) {
481                 index += localLength;
482                 length -= localLength;
483                 readBytes += localLength;
484                 i ++;
485             } else {
486                 index += localReadBytes;
487                 length -= localReadBytes;
488                 readBytes += localReadBytes;
489             }
490         } while (length > 0);
491 
492         return readBytes;
493     }
494 
495     public int setBytes(int index, ScatteringByteChannel in, int length)
496             throws IOException {
497         int componentId = componentId(index);
498         if (index > capacity() - length) {
499             throw new IndexOutOfBoundsException("Too many bytes to write - Needs "
500                     + (index + length) + ", maximum is " + capacity());
501         }
502 
503         int i = componentId;
504         int readBytes = 0;
505         do {
506             ChannelBuffer s = components[i];
507             int adjustment = indices[i];
508             int localLength = Math.min(length, s.capacity() - (index - adjustment));
509             int localReadBytes = s.setBytes(index - adjustment, in, localLength);
510 
511             if (localReadBytes == 0) {
512                 break;
513             }
514 
515             if (localReadBytes < 0) {
516                 if (readBytes == 0) {
517                     return -1;
518                 } else {
519                     break;
520                 }
521             }
522 
523             if (localReadBytes == localLength) {
524                 index += localLength;
525                 length -= localLength;
526                 readBytes += localLength;
527                 i ++;
528             } else {
529                 index += localReadBytes;
530                 length -= localReadBytes;
531                 readBytes += localReadBytes;
532             }
533         } while (length > 0);
534 
535         return readBytes;
536     }
537 
538     public ChannelBuffer duplicate() {
539         ChannelBuffer duplicate = new CompositeChannelBuffer(this);
540         duplicate.setIndex(readerIndex(), writerIndex());
541         return duplicate;
542     }
543 
544     public ChannelBuffer copy(int index, int length) {
545         int componentId = componentId(index);
546         if (index > capacity() - length) {
547             throw new IndexOutOfBoundsException("Too many bytes to copy - Needs "
548                     + (index + length) + ", maximum is " + capacity());
549         }
550 
551         ChannelBuffer dst = factory().getBuffer(order(), length);
552         copyTo(index, length, componentId, dst);
553         return dst;
554     }
555 
556     private void copyTo(int index, int length, int componentId, ChannelBuffer dst) {
557         int dstIndex = 0;
558         int i = componentId;
559 
560         while (length > 0) {
561             ChannelBuffer s = components[i];
562             int adjustment = indices[i];
563             int localLength = Math.min(length, s.capacity() - (index - adjustment));
564             s.getBytes(index - adjustment, dst, dstIndex, localLength);
565             index += localLength;
566             dstIndex += localLength;
567             length -= localLength;
568             i ++;
569         }
570 
571         dst.writerIndex(dst.capacity());
572     }
573 
574     /**
575     * Returns the {@link ChannelBuffer} portion of this {@link CompositeChannelBuffer} that
576     * contains the specified {@code index}. <strong>This is an expert method!</strong>
577     *
578     * <p>
579     * Please note that since a {@link CompositeChannelBuffer} is made up of
580     * multiple {@link ChannelBuffer}s, this does <em>not</em> return the full buffer.
581     * Instead, it only returns a portion of the composite buffer where the
582     * index is located
583     * </p>
584     *
585     *
586     * @param index The {@code index} to search for and include in the returned {@link ChannelBuffer}
587     * @return The {@link ChannelBuffer} that contains the specified {@code index}
588     * @throws IndexOutOfBoundsException when the specified {@code index} is
589     * less than zero, or larger than {@code capacity()}
590     */
591     public ChannelBuffer getBuffer(int index) throws IndexOutOfBoundsException {
592         if (index < 0 || index >= capacity()) {
593             throw new IndexOutOfBoundsException("Invalid index: " + index
594                     + " - Bytes needed: " + index + ", maximum is "
595                     + capacity());
596         }
597 
598         //Return the component byte buffer
599         return components[componentId(index)];
600 
601     }
602 
603     public ChannelBuffer slice(int index, int length) {
604         if (index == 0) {
605             if (length == 0) {
606                 return ChannelBuffers.EMPTY_BUFFER;
607             }
608         } else if (index < 0 || index > capacity() - length) {
609             throw new IndexOutOfBoundsException("Invalid index: " + index
610                     + " - Bytes needed: " + (index + length) + ", maximum is "
611                     + capacity());
612         } else if (length == 0) {
613             return ChannelBuffers.EMPTY_BUFFER;
614         }
615 
616         List<ChannelBuffer> components = decompose(index, length);
617         switch (components.size()) {
618         case 0:
619             return ChannelBuffers.EMPTY_BUFFER;
620         case 1:
621             return components.get(0);
622         default:
623             return new CompositeChannelBuffer(order(), components, gathering);
624         }
625     }
626 
627     public ByteBuffer toByteBuffer(int index, int length) {
628         if (components.length == 1) {
629             return components[0].toByteBuffer(index, length);
630         }
631 
632         ByteBuffer[] buffers = toByteBuffers(index, length);
633         ByteBuffer merged = ByteBuffer.allocate(length).order(order());
634         for (ByteBuffer b: buffers) {
635             merged.put(b);
636         }
637         merged.flip();
638         return merged;
639     }
640 
641     @Override
642     public ByteBuffer[] toByteBuffers(int index, int length) {
643         int componentId = componentId(index);
644         if (index + length > capacity()) {
645             throw new IndexOutOfBoundsException("Too many bytes to convert - Needs"
646                     + (index + length) + ", maximum is " + capacity());
647         }
648 
649         List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(components.length);
650 
651         int i = componentId;
652         while (length > 0) {
653             ChannelBuffer s = components[i];
654             int adjustment = indices[i];
655             int localLength = Math.min(length, s.capacity() - (index - adjustment));
656             buffers.add(s.toByteBuffer(index - adjustment, localLength));
657             index += localLength;
658             length -= localLength;
659             i ++;
660         }
661 
662         return buffers.toArray(new ByteBuffer[buffers.size()]);
663     }
664 
665     private int componentId(int index) {
666         int lastComponentId = lastAccessedComponentId;
667         if (index >= indices[lastComponentId]) {
668             if (index < indices[lastComponentId + 1]) {
669                 return lastComponentId;
670             }
671 
672             // Search right
673             for (int i = lastComponentId + 1; i < components.length; i ++) {
674                 if (index < indices[i + 1]) {
675                     lastAccessedComponentId = i;
676                     return i;
677                 }
678             }
679         } else {
680             // Search left
681             for (int i = lastComponentId - 1; i >= 0; i --) {
682                 if (index >= indices[i]) {
683                     lastAccessedComponentId = i;
684                     return i;
685                 }
686             }
687         }
688 
689         throw new IndexOutOfBoundsException("Invalid index: " + index + ", maximum: " + indices.length);
690     }
691 
692     @Override
693     public void discardReadBytes() {
694         // Only the bytes between readerIndex and writerIndex will be kept.
695         // New readerIndex and writerIndex will become 0 and
696         // (previous writerIndex - previous readerIndex) respectively.
697 
698         final int localReaderIndex = this.readerIndex();
699         if (localReaderIndex == 0) {
700             return;
701         }
702         int localWriterIndex = this.writerIndex();
703 
704         final int bytesToMove = capacity() - localReaderIndex;
705         List<ChannelBuffer> list = decompose(localReaderIndex, bytesToMove);
706 
707         // If the list is empty we need to assign a new one because
708         // we get a List that is immutable.
709         //
710         // See https://github.com/netty/netty/issues/325
711         if (list.isEmpty()) {
712             list = new ArrayList<ChannelBuffer>(1);
713         }
714         // Add a new buffer so that the capacity of this composite buffer does
715         // not decrease due to the discarded components.
716         // XXX Might create too many components if discarded by small amount.
717         final ChannelBuffer padding = ChannelBuffers.buffer(order(), localReaderIndex);
718         padding.writerIndex(localReaderIndex);
719         list.add(padding);
720 
721         // Reset the index markers to get the index marker values.
722         int localMarkedReaderIndex = localReaderIndex;
723         try {
724             resetReaderIndex();
725             localMarkedReaderIndex = this.readerIndex();
726         } catch (IndexOutOfBoundsException e) {
727             // ignore
728         }
729         int localMarkedWriterIndex = localWriterIndex;
730         try {
731             resetWriterIndex();
732             localMarkedWriterIndex = this.writerIndex();
733         } catch (IndexOutOfBoundsException e) {
734             // ignore
735         }
736 
737         setComponents(list);
738 
739         // reset marked Indexes
740         localMarkedReaderIndex = Math.max(localMarkedReaderIndex - localReaderIndex, 0);
741         localMarkedWriterIndex = Math.max(localMarkedWriterIndex - localReaderIndex, 0);
742         setIndex(localMarkedReaderIndex, localMarkedWriterIndex);
743         markReaderIndex();
744         markWriterIndex();
745         // reset real indexes
746         localWriterIndex = Math.max(localWriterIndex - localReaderIndex, 0);
747         setIndex(0, localWriterIndex);
748     }
749 
750     @Override
751     public String toString() {
752         String result = super.toString();
753         result = result.substring(0, result.length() - 1);
754         return result + ", components=" + components.length + ")";
755     }
756 }