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 }