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 org.jboss.netty.util.internal.DetectionUtil;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.nio.ByteBuffer;
24  import java.nio.ByteOrder;
25  import java.nio.channels.GatheringByteChannel;
26  import java.nio.channels.ScatteringByteChannel;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.List;
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         if (index > capacity() - length || dstIndex > dst.length - length) {
232             throw new IndexOutOfBoundsException("Too many bytes to read - Needs "
233                     + (index + length) + ", maximum is " + capacity() + " or "
234                     + dst.length);
235         }
236         if (index < 0) {
237             throw new IndexOutOfBoundsException("Index must be >= 0");
238         }
239         if (length == 0) {
240             return;
241         }
242 
243         int componentId = componentId(index);
244 
245         int i = componentId;
246         while (length > 0) {
247             ChannelBuffer s = components[i];
248             int adjustment = indices[i];
249             int localLength = Math.min(length, s.capacity() - (index - adjustment));
250             s.getBytes(index - adjustment, dst, dstIndex, localLength);
251             index += localLength;
252             dstIndex += localLength;
253             length -= localLength;
254             i ++;
255         }
256     }
257 
258     public void getBytes(int index, ByteBuffer dst) {
259         int componentId = componentId(index);
260         int limit = dst.limit();
261         int length = dst.remaining();
262         if (index > capacity() - length) {
263             throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
264                     + (index + length) + ", maximum is " + capacity());
265         }
266         if (index < 0) {
267             throw new IndexOutOfBoundsException("Index must be >= 0");
268         }
269         int i = componentId;
270         try {
271             while (length > 0) {
272                 ChannelBuffer s = components[i];
273                 int adjustment = indices[i];
274                 int localLength = Math.min(length, s.capacity() - (index - adjustment));
275                 dst.limit(dst.position() + localLength);
276                 s.getBytes(index - adjustment, dst);
277                 index += localLength;
278                 length -= localLength;
279                 i ++;
280             }
281         } finally {
282             dst.limit(limit);
283         }
284     }
285 
286     public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) {
287         if (index > capacity() - length || dstIndex > dst.capacity() - length) {
288             throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
289                     + (index + length) + " or " + (dstIndex + length) + ", maximum is "
290                     + capacity() + " or " + dst.capacity());
291         }
292         if (index < 0) {
293             throw new IndexOutOfBoundsException("Index must be >= 0");
294         }
295         if (length == 0) {
296             return;
297         }
298         int i = componentId(index);
299         while (length > 0) {
300             ChannelBuffer s = components[i];
301             int adjustment = indices[i];
302             int localLength = Math.min(length, s.capacity() - (index - adjustment));
303             s.getBytes(index - adjustment, dst, dstIndex, localLength);
304             index += localLength;
305             dstIndex += localLength;
306             length -= localLength;
307             i ++;
308         }
309     }
310 
311     public int getBytes(int index, GatheringByteChannel out, int length)
312             throws IOException {
313 
314         if (useGathering()) {
315             return (int) out.write(toByteBuffers(index, length));
316         }
317 
318         // XXX Gathering write is not supported because of a known issue.
319         //     See http://bugs.sun.com/view_bug.do?bug_id=6210541
320         //     This issue appeared in 2004 and is still unresolved!?
321         return out.write(toByteBuffer(index, length));
322     }
323 
324     public void getBytes(int index, OutputStream out, int length)
325             throws IOException {
326         if (index > capacity() - length) {
327             throw new IndexOutOfBoundsException("Too many bytes to be read - needs "
328                     + (index + length) + ", maximum of " + capacity());
329         }
330         if (index < 0) {
331             throw new IndexOutOfBoundsException("Index must be >= 0");
332         }
333         if (length == 0) {
334             return;
335         }
336         int i = componentId(index);
337         while (length > 0) {
338             ChannelBuffer s = components[i];
339             int adjustment = indices[i];
340             int localLength = Math.min(length, s.capacity() - (index - adjustment));
341             s.getBytes(index - adjustment, out, localLength);
342             index += localLength;
343             length -= localLength;
344             i ++;
345         }
346     }
347 
348     public void setByte(int index, int value) {
349         int componentId = componentId(index);
350         components[componentId].setByte(index - indices[componentId], value);
351     }
352 
353     public void setShort(int index, int value) {
354         int componentId = componentId(index);
355         if (index + 2 <= indices[componentId + 1]) {
356             components[componentId].setShort(index - indices[componentId], value);
357         } else if (order() == ByteOrder.BIG_ENDIAN) {
358             setByte(index, (byte) (value >>> 8));
359             setByte(index + 1, (byte) value);
360         } else {
361             setByte(index    , (byte) value);
362             setByte(index + 1, (byte) (value >>> 8));
363         }
364     }
365 
366     public void setMedium(int index, int value) {
367         int componentId = componentId(index);
368         if (index + 3 <= indices[componentId + 1]) {
369             components[componentId].setMedium(index - indices[componentId], value);
370         } else if (order() == ByteOrder.BIG_ENDIAN) {
371             setShort(index, (short) (value >> 8));
372             setByte(index + 2, (byte) value);
373         } else {
374             setShort(index   , (short) value);
375             setByte(index + 2, (byte) (value >>> 16));
376         }
377     }
378 
379     public void setInt(int index, int value) {
380         int componentId = componentId(index);
381         if (index + 4 <= indices[componentId + 1]) {
382             components[componentId].setInt(index - indices[componentId], value);
383         } else if (order() == ByteOrder.BIG_ENDIAN) {
384             setShort(index, (short) (value >>> 16));
385             setShort(index + 2, (short) value);
386         } else {
387             setShort(index    , (short) value);
388             setShort(index + 2, (short) (value >>> 16));
389         }
390     }
391 
392     public void setLong(int index, long value) {
393         int componentId = componentId(index);
394         if (index + 8 <= indices[componentId + 1]) {
395             components[componentId].setLong(index - indices[componentId], value);
396         } else if (order() == ByteOrder.BIG_ENDIAN) {
397             setInt(index, (int) (value >>> 32));
398             setInt(index + 4, (int) value);
399         } else {
400             setInt(index    , (int) value);
401             setInt(index + 4, (int) (value >>> 32));
402         }
403     }
404 
405     public void setBytes(int index, byte[] src, int srcIndex, int length) {
406         int componentId = componentId(index);
407         if (index > capacity() - length || srcIndex > src.length - length) {
408             throw new IndexOutOfBoundsException("Too many bytes to read - needs "
409                     + (index + length) + " or " + (srcIndex + length) + ", maximum is "
410                     + capacity() + " or " + src.length);
411         }
412 
413         int i = componentId;
414         while (length > 0) {
415             ChannelBuffer s = components[i];
416             int adjustment = indices[i];
417             int localLength = Math.min(length, s.capacity() - (index - adjustment));
418             s.setBytes(index - adjustment, src, srcIndex, localLength);
419             index += localLength;
420             srcIndex += localLength;
421             length -= localLength;
422             i ++;
423         }
424     }
425 
426     public void setBytes(int index, ByteBuffer src) {
427         int componentId = componentId(index);
428         int limit = src.limit();
429         int length = src.remaining();
430         if (index > capacity() - length) {
431             throw new IndexOutOfBoundsException("Too many bytes to be written - Needs "
432                     + (index + length) + ", maximum is " + capacity());
433         }
434 
435         int i = componentId;
436         try {
437             while (length > 0) {
438                 ChannelBuffer s = components[i];
439                 int adjustment = indices[i];
440                 int localLength = Math.min(length, s.capacity() - (index - adjustment));
441                 src.limit(src.position() + localLength);
442                 s.setBytes(index - adjustment, src);
443                 index += localLength;
444                 length -= localLength;
445                 i ++;
446             }
447         } finally {
448             src.limit(limit);
449         }
450     }
451 
452     public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) {
453         int componentId = componentId(index);
454         if (index > capacity() - length || srcIndex > src.capacity() - length) {
455             throw new IndexOutOfBoundsException("Too many bytes to be written - Needs "
456                     + (index + length) + " or " + (srcIndex + length) + ", maximum is "
457                     + capacity() + " or " + src.capacity());
458         }
459 
460         int i = componentId;
461         while (length > 0) {
462             ChannelBuffer s = components[i];
463             int adjustment = indices[i];
464             int localLength = Math.min(length, s.capacity() - (index - adjustment));
465             s.setBytes(index - adjustment, src, srcIndex, localLength);
466             index += localLength;
467             srcIndex += localLength;
468             length -= localLength;
469             i ++;
470         }
471     }
472 
473     public int setBytes(int index, InputStream in, int length)
474             throws IOException {
475         int componentId = componentId(index);
476         if (index > capacity() - length) {
477             throw new IndexOutOfBoundsException("Too many bytes to write - Needs "
478                     + (index + length) + ", maximum is " + capacity());
479         }
480 
481         int i = componentId;
482         int readBytes = 0;
483 
484         do {
485             ChannelBuffer s = components[i];
486             int adjustment = indices[i];
487             int localLength = Math.min(length, s.capacity() - (index - adjustment));
488             int localReadBytes = s.setBytes(index - adjustment, in, localLength);
489             if (localReadBytes < 0) {
490                 if (readBytes == 0) {
491                     return -1;
492                 } else {
493                     break;
494                 }
495             }
496 
497             if (localReadBytes == localLength) {
498                 index += localLength;
499                 length -= localLength;
500                 readBytes += localLength;
501                 i ++;
502             } else {
503                 index += localReadBytes;
504                 length -= localReadBytes;
505                 readBytes += localReadBytes;
506             }
507         } while (length > 0);
508 
509         return readBytes;
510     }
511 
512     public int setBytes(int index, ScatteringByteChannel in, int length)
513             throws IOException {
514         int componentId = componentId(index);
515         if (index > capacity() - length) {
516             throw new IndexOutOfBoundsException("Too many bytes to write - Needs "
517                     + (index + length) + ", maximum is " + capacity());
518         }
519 
520         int i = componentId;
521         int readBytes = 0;
522         do {
523             ChannelBuffer s = components[i];
524             int adjustment = indices[i];
525             int localLength = Math.min(length, s.capacity() - (index - adjustment));
526             int localReadBytes = s.setBytes(index - adjustment, in, localLength);
527 
528             if (localReadBytes == 0) {
529                 break;
530             }
531 
532             if (localReadBytes < 0) {
533                 if (readBytes == 0) {
534                     return -1;
535                 } else {
536                     break;
537                 }
538             }
539 
540             if (localReadBytes == localLength) {
541                 index += localLength;
542                 length -= localLength;
543                 readBytes += localLength;
544                 i ++;
545             } else {
546                 index += localReadBytes;
547                 length -= localReadBytes;
548                 readBytes += localReadBytes;
549             }
550         } while (length > 0);
551 
552         return readBytes;
553     }
554 
555     public ChannelBuffer duplicate() {
556         ChannelBuffer duplicate = new CompositeChannelBuffer(this);
557         duplicate.setIndex(readerIndex(), writerIndex());
558         return duplicate;
559     }
560 
561     public ChannelBuffer copy(int index, int length) {
562         int componentId = componentId(index);
563         if (index > capacity() - length) {
564             throw new IndexOutOfBoundsException("Too many bytes to copy - Needs "
565                     + (index + length) + ", maximum is " + capacity());
566         }
567 
568         ChannelBuffer dst = factory().getBuffer(order(), length);
569         copyTo(index, length, componentId, dst);
570         return dst;
571     }
572 
573     private void copyTo(int index, int length, int componentId, ChannelBuffer dst) {
574         int dstIndex = 0;
575         int i = componentId;
576 
577         while (length > 0) {
578             ChannelBuffer s = components[i];
579             int adjustment = indices[i];
580             int localLength = Math.min(length, s.capacity() - (index - adjustment));
581             s.getBytes(index - adjustment, dst, dstIndex, localLength);
582             index += localLength;
583             dstIndex += localLength;
584             length -= localLength;
585             i ++;
586         }
587 
588         dst.writerIndex(dst.capacity());
589     }
590 
591     /**
592     * Returns the {@link ChannelBuffer} portion of this {@link CompositeChannelBuffer} that
593     * contains the specified {@code index}. <strong>This is an expert method!</strong>
594     *
595     * <p>
596     * Please note that since a {@link CompositeChannelBuffer} is made up of
597     * multiple {@link ChannelBuffer}s, this does <em>not</em> return the full buffer.
598     * Instead, it only returns a portion of the composite buffer where the
599     * index is located
600     * </p>
601     *
602     *
603     * @param index The {@code index} to search for and include in the returned {@link ChannelBuffer}
604     * @return The {@link ChannelBuffer} that contains the specified {@code index}
605     * @throws IndexOutOfBoundsException when the specified {@code index} is
606     * less than zero, or larger than {@code capacity()}
607     */
608     public ChannelBuffer getBuffer(int index) {
609         if (index < 0 || index >= capacity()) {
610             throw new IndexOutOfBoundsException("Invalid index: " + index
611                     + " - Bytes needed: " + index + ", maximum is "
612                     + capacity());
613         }
614 
615         //Return the component byte buffer
616         return components[componentId(index)];
617 
618     }
619 
620     public ChannelBuffer slice(int index, int length) {
621         if (index == 0) {
622             if (length == 0) {
623                 return ChannelBuffers.EMPTY_BUFFER;
624             }
625         } else if (index < 0 || index > capacity() - length) {
626             throw new IndexOutOfBoundsException("Invalid index: " + index
627                     + " - Bytes needed: " + (index + length) + ", maximum is "
628                     + capacity());
629         } else if (length == 0) {
630             return ChannelBuffers.EMPTY_BUFFER;
631         }
632 
633         List<ChannelBuffer> components = decompose(index, length);
634         switch (components.size()) {
635         case 0:
636             return ChannelBuffers.EMPTY_BUFFER;
637         case 1:
638             return components.get(0);
639         default:
640             return new CompositeChannelBuffer(order(), components, gathering);
641         }
642     }
643 
644     public ByteBuffer toByteBuffer(int index, int length) {
645         if (components.length == 1) {
646             return components[0].toByteBuffer(index, length);
647         }
648 
649         ByteBuffer[] buffers = toByteBuffers(index, length);
650         ByteBuffer merged = ByteBuffer.allocate(length).order(order());
651         for (ByteBuffer b: buffers) {
652             merged.put(b);
653         }
654         merged.flip();
655         return merged;
656     }
657 
658     @Override
659     public ByteBuffer[] toByteBuffers(int index, int length) {
660         if (index + length > capacity()) {
661             throw new IndexOutOfBoundsException("Too many bytes to convert - Needs"
662                     + (index + length) + ", maximum is " + capacity());
663         }
664         if (index < 0) {
665             throw new IndexOutOfBoundsException("Index must be >= 0");
666         }
667         if (length == 0) {
668             return new ByteBuffer[0];
669         }
670 
671         List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(components.length);
672 
673         int i = componentId(index);
674         while (length > 0) {
675             ChannelBuffer s = components[i];
676             int adjustment = indices[i];
677             int localLength = Math.min(length, s.capacity() - (index - adjustment));
678             buffers.add(s.toByteBuffer(index - adjustment, localLength));
679             index += localLength;
680             length -= localLength;
681             i ++;
682         }
683 
684         return buffers.toArray(new ByteBuffer[buffers.size()]);
685     }
686 
687     private int componentId(int index) {
688         int lastComponentId = lastAccessedComponentId;
689         if (index >= indices[lastComponentId]) {
690             if (index < indices[lastComponentId + 1]) {
691                 return lastComponentId;
692             }
693 
694             // Search right
695             for (int i = lastComponentId + 1; i < components.length; i ++) {
696                 if (index < indices[i + 1]) {
697                     lastAccessedComponentId = i;
698                     return i;
699                 }
700             }
701         } else {
702             // Search left
703             for (int i = lastComponentId - 1; i >= 0; i --) {
704                 if (index >= indices[i]) {
705                     lastAccessedComponentId = i;
706                     return i;
707                 }
708             }
709         }
710 
711         throw new IndexOutOfBoundsException("Invalid index: " + index + ", maximum: " + indices.length);
712     }
713 
714     @Override
715     public void discardReadBytes() {
716         // Only the bytes between readerIndex and writerIndex will be kept.
717         // New readerIndex and writerIndex will become 0 and
718         // (previous writerIndex - previous readerIndex) respectively.
719 
720         final int localReaderIndex = readerIndex();
721         if (localReaderIndex == 0) {
722             return;
723         }
724         int localWriterIndex = writerIndex();
725 
726         final int bytesToMove = capacity() - localReaderIndex;
727         List<ChannelBuffer> list = decompose(localReaderIndex, bytesToMove);
728 
729         // If the list is empty we need to assign a new one because
730         // we get a List that is immutable.
731         //
732         // See https://github.com/netty/netty/issues/325
733         if (list.isEmpty()) {
734             list = new ArrayList<ChannelBuffer>(1);
735         }
736         // Add a new buffer so that the capacity of this composite buffer does
737         // not decrease due to the discarded components.
738         // XXX Might create too many components if discarded by small amount.
739         final ChannelBuffer padding = ChannelBuffers.buffer(order(), localReaderIndex);
740         padding.writerIndex(localReaderIndex);
741         list.add(padding);
742 
743         // Reset the index markers to get the index marker values.
744         int localMarkedReaderIndex = localReaderIndex;
745         try {
746             resetReaderIndex();
747             localMarkedReaderIndex = readerIndex();
748         } catch (IndexOutOfBoundsException e) {
749             // ignore
750         }
751         int localMarkedWriterIndex = localWriterIndex;
752         try {
753             resetWriterIndex();
754             localMarkedWriterIndex = writerIndex();
755         } catch (IndexOutOfBoundsException e) {
756             // ignore
757         }
758 
759         setComponents(list);
760 
761         // reset marked Indexes
762         localMarkedReaderIndex = Math.max(localMarkedReaderIndex - localReaderIndex, 0);
763         localMarkedWriterIndex = Math.max(localMarkedWriterIndex - localReaderIndex, 0);
764         setIndex(localMarkedReaderIndex, localMarkedWriterIndex);
765         markReaderIndex();
766         markWriterIndex();
767         // reset real indexes
768         localWriterIndex = Math.max(localWriterIndex - localReaderIndex, 0);
769         setIndex(0, localWriterIndex);
770     }
771 
772     @Override
773     public String toString() {
774         String result = super.toString();
775         result = result.substring(0, result.length() - 1);
776         return result + ", components=" + components.length + ')';
777     }
778 }