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 io.netty.buffer;
17
18 import io.netty.util.AsciiString;
19 import io.netty.util.ByteProcessor;
20 import io.netty.util.CharsetUtil;
21 import io.netty.util.IllegalReferenceCountException;
22 import io.netty.util.concurrent.FastThreadLocal;
23 import io.netty.util.internal.MathUtil;
24 import io.netty.util.internal.ObjectPool;
25 import io.netty.util.internal.ObjectPool.Handle;
26 import io.netty.util.internal.ObjectPool.ObjectCreator;
27 import io.netty.util.internal.PlatformDependent;
28 import io.netty.util.internal.StringUtil;
29 import io.netty.util.internal.SystemPropertyUtil;
30 import io.netty.util.internal.logging.InternalLogger;
31 import io.netty.util.internal.logging.InternalLoggerFactory;
32
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.nio.CharBuffer;
38 import java.nio.charset.CharacterCodingException;
39 import java.nio.charset.Charset;
40 import java.nio.charset.CharsetDecoder;
41 import java.nio.charset.CharsetEncoder;
42 import java.nio.charset.CoderResult;
43 import java.nio.charset.CodingErrorAction;
44 import java.util.Arrays;
45 import java.util.Locale;
46
47 import static io.netty.util.internal.MathUtil.isOutOfBounds;
48 import static io.netty.util.internal.ObjectUtil.checkNotNull;
49 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
50 import static io.netty.util.internal.StringUtil.NEWLINE;
51 import static io.netty.util.internal.StringUtil.isSurrogate;
52
53 /**
54  * A collection of utility methods that is related with handling {@link ByteBuf},
55  * such as the generation of hex dump and swapping an integer's byte order.
56  */

57 public final class ByteBufUtil {
58
59     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
60     private static final FastThreadLocal<byte[]> BYTE_ARRAYS = new FastThreadLocal<byte[]>() {
61         @Override
62         protected byte[] initialValue() throws Exception {
63             return PlatformDependent.allocateUninitializedArray(MAX_TL_ARRAY_LEN);
64         }
65     };
66
67     private static final byte WRITE_UTF_UNKNOWN = (byte) '?';
68     private static final int MAX_CHAR_BUFFER_SIZE;
69     private static final int THREAD_LOCAL_BUFFER_SIZE;
70     private static final int MAX_BYTES_PER_CHAR_UTF8 =
71             (int) CharsetUtil.encoder(CharsetUtil.UTF_8).maxBytesPerChar();
72
73     static final int WRITE_CHUNK_SIZE = 8192;
74     static final ByteBufAllocator DEFAULT_ALLOCATOR;
75
76     static {
77         String allocType = SystemPropertyUtil.get(
78                 "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
79         allocType = allocType.toLowerCase(Locale.US).trim();
80
81         ByteBufAllocator alloc;
82         if ("unpooled".equals(allocType)) {
83             alloc = UnpooledByteBufAllocator.DEFAULT;
84             logger.debug("-Dio.netty.allocator.type: {}", allocType);
85         } else if ("pooled".equals(allocType)) {
86             alloc = PooledByteBufAllocator.DEFAULT;
87             logger.debug("-Dio.netty.allocator.type: {}", allocType);
88         } else {
89             alloc = PooledByteBufAllocator.DEFAULT;
90             logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
91         }
92
93         DEFAULT_ALLOCATOR = alloc;
94
95         THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 0);
96         logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
97
98         MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024);
99         logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
100     }
101
102     static final int MAX_TL_ARRAY_LEN = 1024;
103
104     /**
105      * Allocates a new array if minLength > {@link ByteBufUtil#MAX_TL_ARRAY_LEN}
106      */

107     static byte[] threadLocalTempArray(int minLength) {
108         return minLength <= MAX_TL_ARRAY_LEN ? BYTE_ARRAYS.get()
109             : PlatformDependent.allocateUninitializedArray(minLength);
110     }
111
112     /**
113      * @return whether the specified buffer has a nonzero ref count
114      */

115     public static boolean isAccessible(ByteBuf buffer) {
116         return buffer.isAccessible();
117     }
118
119     /**
120      * @throws IllegalReferenceCountException if the buffer has a zero ref count
121      * @return the passed in buffer
122      */

123     public static ByteBuf ensureAccessible(ByteBuf buffer) {
124         if (!buffer.isAccessible()) {
125             throw new IllegalReferenceCountException(buffer.refCnt());
126         }
127         return buffer;
128     }
129
130     /**
131      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
132      * of the specified buffer's readable bytes.
133      */

134     public static String hexDump(ByteBuf buffer) {
135         return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
136     }
137
138     /**
139      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
140      * of the specified buffer's sub-region.
141      */

142     public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
143         return HexUtil.hexDump(buffer, fromIndex, length);
144     }
145
146     /**
147      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
148      * of the specified byte array.
149      */

150     public static String hexDump(byte[] array) {
151         return hexDump(array, 0, array.length);
152     }
153
154     /**
155      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
156      * of the specified byte array's sub-region.
157      */

158     public static String hexDump(byte[] array, int fromIndex, int length) {
159         return HexUtil.hexDump(array, fromIndex, length);
160     }
161
162     /**
163      * Decode a 2-digit hex byte from within a string.
164      */

165     public static byte decodeHexByte(CharSequence s, int pos) {
166         return StringUtil.decodeHexByte(s, pos);
167     }
168
169     /**
170      * Decodes a string generated by {@link #hexDump(byte[])}
171      */

172     public static byte[] decodeHexDump(CharSequence hexDump) {
173         return StringUtil.decodeHexDump(hexDump, 0, hexDump.length());
174     }
175
176     /**
177      * Decodes part of a string generated by {@link #hexDump(byte[])}
178      */

179     public static byte[] decodeHexDump(CharSequence hexDump, int fromIndex, int length) {
180         return StringUtil.decodeHexDump(hexDump, fromIndex, length);
181     }
182
183     /**
184      * Used to determine if the return value of {@link ByteBuf#ensureWritable(intboolean)} means that there is
185      * adequate space and a write operation will succeed.
186      * @param ensureWritableResult The return value from {@link ByteBuf#ensureWritable(intboolean)}.
187      * @return {@code trueif {@code ensureWritableResult} means that there is adequate space and a write operation
188      * will succeed.
189      */

190     public static boolean ensureWritableSuccess(int ensureWritableResult) {
191         return ensureWritableResult == 0 || ensureWritableResult == 2;
192     }
193
194     /**
195      * Calculates the hash code of the specified buffer.  This method is
196      * useful when implementing a new buffer type.
197      */

198     public static int hashCode(ByteBuf buffer) {
199         final int aLen = buffer.readableBytes();
200         final int intCount = aLen >>> 2;
201         final int byteCount = aLen & 3;
202
203         int hashCode = EmptyByteBuf.EMPTY_BYTE_BUF_HASH_CODE;
204         int arrayIndex = buffer.readerIndex();
205         if (buffer.order() == ByteOrder.BIG_ENDIAN) {
206             for (int i = intCount; i > 0; i --) {
207                 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
208                 arrayIndex += 4;
209             }
210         } else {
211             for (int i = intCount; i > 0; i --) {
212                 hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
213                 arrayIndex += 4;
214             }
215         }
216
217         for (int i = byteCount; i > 0; i --) {
218             hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
219         }
220
221         if (hashCode == 0) {
222             hashCode = 1;
223         }
224
225         return hashCode;
226     }
227
228     /**
229      * Returns the reader index of needle in haystack, or -1 if needle is not in haystack.
230      */

231     public static int indexOf(ByteBuf needle, ByteBuf haystack) {
232         // TODO: maybe use Boyer Moore for efficiency.
233         int attempts = haystack.readableBytes() - needle.readableBytes() + 1;
234         for (int i = 0; i < attempts; i++) {
235             if (equals(needle, needle.readerIndex(),
236                        haystack, haystack.readerIndex() + i,
237                        needle.readableBytes())) {
238                 return haystack.readerIndex() + i;
239             }
240         }
241         return -1;
242     }
243
244     /**
245      * Returns {@code trueif and only if the two specified buffers are
246      * identical to each other for {@code length} bytes starting at {@code aStartIndex}
247      * index for the {@code a} buffer and {@code bStartIndex} index for the {@code b} buffer.
248      * A more compact way to express this is:
249      * <p>
250      * {@code a[aStartIndex : aStartIndex + length] == b[bStartIndex : bStartIndex + length]}
251      */

252     public static boolean equals(ByteBuf a, int aStartIndex, ByteBuf b, int bStartIndex, int length) {
253         if (aStartIndex < 0 || bStartIndex < 0 || length < 0) {
254             throw new IllegalArgumentException("All indexes and lengths must be non-negative");
255         }
256         if (a.writerIndex() - length < aStartIndex || b.writerIndex() - length < bStartIndex) {
257             return false;
258         }
259
260         final int longCount = length >>> 3;
261         final int byteCount = length & 7;
262
263         if (a.order() == b.order()) {
264             for (int i = longCount; i > 0; i --) {
265                 if (a.getLong(aStartIndex) != b.getLong(bStartIndex)) {
266                     return false;
267                 }
268                 aStartIndex += 8;
269                 bStartIndex += 8;
270             }
271         } else {
272             for (int i = longCount; i > 0; i --) {
273                 if (a.getLong(aStartIndex) != swapLong(b.getLong(bStartIndex))) {
274                     return false;
275                 }
276                 aStartIndex += 8;
277                 bStartIndex += 8;
278             }
279         }
280
281         for (int i = byteCount; i > 0; i --) {
282             if (a.getByte(aStartIndex) != b.getByte(bStartIndex)) {
283                 return false;
284             }
285             aStartIndex ++;
286             bStartIndex ++;
287         }
288
289         return true;
290     }
291
292     /**
293      * Returns {@code trueif and only if the two specified buffers are
294      * identical to each other as described in {@link ByteBuf#equals(Object)}.
295      * This method is useful when implementing a new buffer type.
296      */

297     public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
298         final int aLen = bufferA.readableBytes();
299         if (aLen != bufferB.readableBytes()) {
300             return false;
301         }
302         return equals(bufferA, bufferA.readerIndex(), bufferB, bufferB.readerIndex(), aLen);
303     }
304
305     /**
306      * Compares the two specified buffers as described in {@link ByteBuf#compareTo(ByteBuf)}.
307      * This method is useful when implementing a new buffer type.
308      */

309     public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
310         final int aLen = bufferA.readableBytes();
311         final int bLen = bufferB.readableBytes();
312         final int minLength = Math.min(aLen, bLen);
313         final int uintCount = minLength >>> 2;
314         final int byteCount = minLength & 3;
315         int aIndex = bufferA.readerIndex();
316         int bIndex = bufferB.readerIndex();
317
318         if (uintCount > 0) {
319             boolean bufferAIsBigEndian = bufferA.order() == ByteOrder.BIG_ENDIAN;
320             final long res;
321             int uintCountIncrement = uintCount << 2;
322
323             if (bufferA.order() == bufferB.order()) {
324                 res = bufferAIsBigEndian ? compareUintBigEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
325                         compareUintLittleEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
326             } else {
327                 res = bufferAIsBigEndian ? compareUintBigEndianA(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
328                         compareUintBigEndianB(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
329             }
330             if (res != 0) {
331                 // Ensure we not overflow when cast
332                 return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, res));
333             }
334             aIndex += uintCountIncrement;
335             bIndex += uintCountIncrement;
336         }
337
338         for (int aEnd = aIndex + byteCount; aIndex < aEnd; ++aIndex, ++bIndex) {
339             int comp = bufferA.getUnsignedByte(aIndex) - bufferB.getUnsignedByte(bIndex);
340             if (comp != 0) {
341                 return comp;
342             }
343         }
344
345         return aLen - bLen;
346     }
347
348     private static long compareUintBigEndian(
349             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
350         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
351             long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedInt(bIndex);
352             if (comp != 0) {
353                 return comp;
354             }
355         }
356         return 0;
357     }
358
359     private static long compareUintLittleEndian(
360             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
361         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
362             long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedIntLE(bIndex);
363             if (comp != 0) {
364                 return comp;
365             }
366         }
367         return 0;
368     }
369
370     private static long compareUintBigEndianA(
371             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
372         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
373             long comp =  bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedIntLE(bIndex);
374             if (comp != 0) {
375                 return comp;
376             }
377         }
378         return 0;
379     }
380
381     private static long compareUintBigEndianB(
382             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
383         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
384             long comp =  bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedInt(bIndex);
385             if (comp != 0) {
386                 return comp;
387             }
388         }
389         return 0;
390     }
391
392     /**
393      * The default implementation of {@link ByteBuf#indexOf(intintbyte)}.
394      * This method is useful when implementing a new buffer type.
395      */

396     public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
397         if (fromIndex <= toIndex) {
398             return firstIndexOf(buffer, fromIndex, toIndex, value);
399         } else {
400             return lastIndexOf(buffer, fromIndex, toIndex, value);
401         }
402     }
403
404     /**
405      * Toggles the endianness of the specified 16-bit short integer.
406      */

407     public static short swapShort(short value) {
408         return Short.reverseBytes(value);
409     }
410
411     /**
412      * Toggles the endianness of the specified 24-bit medium integer.
413      */

414     public static int swapMedium(int value) {
415         int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
416         if ((swapped & 0x800000) != 0) {
417             swapped |= 0xff000000;
418         }
419         return swapped;
420     }
421
422     /**
423      * Toggles the endianness of the specified 32-bit integer.
424      */

425     public static int swapInt(int value) {
426         return Integer.reverseBytes(value);
427     }
428
429     /**
430      * Toggles the endianness of the specified 64-bit long integer.
431      */

432     public static long swapLong(long value) {
433         return Long.reverseBytes(value);
434     }
435
436     /**
437      * Writes a big-endian 16-bit short integer to the buffer.
438      */

439     @SuppressWarnings("deprecation")
440     public static ByteBuf writeShortBE(ByteBuf buf, int shortValue) {
441         return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeShort(shortValue) : buf.writeShortLE(shortValue);
442     }
443
444     /**
445      * Sets a big-endian 16-bit short integer to the buffer.
446      */

447     @SuppressWarnings("deprecation")
448     public static ByteBuf setShortBE(ByteBuf buf, int index, int shortValue) {
449         return buf.order() == ByteOrder.BIG_ENDIAN? buf.setShort(index, shortValue) : buf.setShortLE(index, shortValue);
450     }
451
452     /**
453      * Writes a big-endian 24-bit medium integer to the buffer.
454      */

455     @SuppressWarnings("deprecation")
456     public static ByteBuf writeMediumBE(ByteBuf buf, int mediumValue) {
457         return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeMedium(mediumValue) : buf.writeMediumLE(mediumValue);
458     }
459
460     /**
461      * Read the given amount of bytes into a new {@link ByteBuf} that is allocated from the {@link ByteBufAllocator}.
462      */

463     public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) {
464         boolean release = true;
465         ByteBuf dst = alloc.buffer(length);
466         try {
467             buffer.readBytes(dst);
468             release = false;
469             return dst;
470         } finally {
471             if (release) {
472                 dst.release();
473             }
474         }
475     }
476
477     private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
478         fromIndex = Math.max(fromIndex, 0);
479         if (fromIndex >= toIndex || buffer.capacity() == 0) {
480             return -1;
481         }
482
483         return buffer.forEachByte(fromIndex, toIndex - fromIndex, new ByteProcessor.IndexOfProcessor(value));
484     }
485
486     private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
487         int capacity = buffer.capacity();
488         fromIndex = Math.min(fromIndex, capacity);
489         if (fromIndex < 0 || capacity == 0) {
490             return -1;
491         }
492
493         return buffer.forEachByteDesc(toIndex, fromIndex - toIndex, new ByteProcessor.IndexOfProcessor(value));
494     }
495
496     private static CharSequence checkCharSequenceBounds(CharSequence seq, int start, int end) {
497         if (MathUtil.isOutOfBounds(start, end - start, seq.length())) {
498             throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end
499                     + ") <= seq.length(" + seq.length() + ')');
500         }
501         return seq;
502     }
503
504     /**
505      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
506      * it to a {@link ByteBuf} allocated with {@code alloc}.
507      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
508      * @param seq The characters to write into a buffer.
509      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoded
510      * result.
511      */

512     public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
513         // UTF-8 uses max. 3 bytes per char, so calculate the worst case.
514         ByteBuf buf = alloc.buffer(utf8MaxBytes(seq));
515         writeUtf8(buf, seq);
516         return buf;
517     }
518
519     /**
520      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
521      * it to a {@link ByteBuf}.
522      * <p>
523      * It behaves like {@link #reserveAndWriteUtf8(ByteBuf, CharSequence, int)} with {@code reserveBytes}
524      * computed by {@link #utf8MaxBytes(CharSequence)}.<br>
525      * This method returns the actual number of bytes written.
526      */

527     public static int writeUtf8(ByteBuf buf, CharSequence seq) {
528         int seqLength = seq.length();
529         return reserveAndWriteUtf8Seq(buf, seq, 0, seqLength, utf8MaxBytes(seqLength));
530     }
531
532     /**
533      * Equivalent to <code>{@link #writeUtf8(ByteBuf, CharSequence) writeUtf8(buf, seq.subSequence(start, end))}</code>
534      * but avoids subsequence object allocation.
535      */

536     public static int writeUtf8(ByteBuf buf, CharSequence seq, int start, int end) {
537         checkCharSequenceBounds(seq, start, end);
538         return reserveAndWriteUtf8Seq(buf, seq, start, end, utf8MaxBytes(end - start));
539     }
540
541     /**
542      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
543      * it into {@code reserveBytes} of a {@link ByteBuf}.
544      * <p>
545      * The {@code reserveBytes} must be computed (ie eagerly using {@link #utf8MaxBytes(CharSequence)}
546      * or exactly with {@link #utf8Bytes(CharSequence)}) to ensure this method to not fail: for performance reasons
547      * the index checks will be performed using just {@code reserveBytes}.<br>
548      * This method returns the actual number of bytes written.
549      */

550     public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int reserveBytes) {
551         return reserveAndWriteUtf8Seq(buf, seq, 0, seq.length(), reserveBytes);
552     }
553
554     /**
555      * Equivalent to <code>{@link #reserveAndWriteUtf8(ByteBuf, CharSequence, int)
556      * reserveAndWriteUtf8(buf, seq.subSequence(start, end), reserveBytes)}</code> but avoids
557      * subsequence object allocation if possible.
558      *
559      * @return actual number of bytes written
560      */

561     public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int start, int end, int reserveBytes) {
562         return reserveAndWriteUtf8Seq(buf, checkCharSequenceBounds(seq, start, end), start, end, reserveBytes);
563     }
564
565     private static int reserveAndWriteUtf8Seq(ByteBuf buf, CharSequence seq, int start, int end, int reserveBytes) {
566         for (;;) {
567             if (buf instanceof WrappedCompositeByteBuf) {
568                 // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling.
569                 buf = buf.unwrap();
570             } else if (buf instanceof AbstractByteBuf) {
571                 AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
572                 byteBuf.ensureWritable0(reserveBytes);
573                 int written = writeUtf8(byteBuf, byteBuf.writerIndex, reserveBytes, seq, start, end);
574                 byteBuf.writerIndex += written;
575                 return written;
576             } else if (buf instanceof WrappedByteBuf) {
577                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
578                 buf = buf.unwrap();
579             } else {
580                 byte[] bytes = seq.subSequence(start, end).toString().getBytes(CharsetUtil.UTF_8);
581                 buf.writeBytes(bytes);
582                 return bytes.length;
583             }
584         }
585     }
586
587     static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes, CharSequence seq, int len) {
588         return writeUtf8(buffer, writerIndex, reservedBytes, seq, 0, len);
589     }
590
591     // Fast-Path implementation
592     static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes,
593                          CharSequence seq, int start, int end) {
594         if (seq instanceof AsciiString) {
595             writeAsciiString(buffer, writerIndex, (AsciiString) seq, start, end);
596             return end - start;
597         }
598         if (PlatformDependent.hasUnsafe()) {
599             if (buffer.hasArray()) {
600                 return unsafeWriteUtf8(buffer.array(), PlatformDependent.byteArrayBaseOffset(),
601                                        buffer.arrayOffset() + writerIndex, seq, start, end);
602             }
603             if (buffer.hasMemoryAddress()) {
604                 return unsafeWriteUtf8(null, buffer.memoryAddress(), writerIndex, seq, start, end);
605             }
606         } else {
607             if (buffer.hasArray()) {
608                 return safeArrayWriteUtf8(buffer.array(), buffer.arrayOffset() + writerIndex, seq, start, end);
609             }
610             if (buffer.isDirect()) {
611                 assert buffer.nioBufferCount() == 1;
612                 final ByteBuffer internalDirectBuffer = buffer.internalNioBuffer(writerIndex, reservedBytes);
613                 final int bufferPosition = internalDirectBuffer.position();
614                 return safeDirectWriteUtf8(internalDirectBuffer, bufferPosition, seq, start, end);
615             }
616         }
617         return safeWriteUtf8(buffer, writerIndex, seq, start, end);
618     }
619
620     // AsciiString Fast-Path implementation - no explicit bound-checks
621     static void writeAsciiString(AbstractByteBuf buffer, int writerIndex, AsciiString seq, int start, int end) {
622         final int begin = seq.arrayOffset() + start;
623         final int length = end - start;
624         if (PlatformDependent.hasUnsafe()) {
625             if (buffer.hasArray()) {
626                 PlatformDependent.copyMemory(seq.array(), begin,
627                                              buffer.array(), buffer.arrayOffset() + writerIndex, length);
628                 return;
629             }
630             if (buffer.hasMemoryAddress()) {
631                 PlatformDependent.copyMemory(seq.array(), begin, buffer.memoryAddress() + writerIndex, length);
632                 return;
633             }
634         }
635         if (buffer.hasArray()) {
636             System.arraycopy(seq.array(), begin, buffer.array(), buffer.arrayOffset() + writerIndex, length);
637             return;
638         }
639         buffer.setBytes(writerIndex, seq.array(), begin, length);
640     }
641
642     // Safe off-heap Fast-Path implementation
643     private static int safeDirectWriteUtf8(ByteBuffer buffer, int writerIndex, CharSequence seq, int start, int end) {
644         assert !(seq instanceof AsciiString);
645         int oldWriterIndex = writerIndex;
646
647         // We can use the _set methods as these not need to do any index checks and reference checks.
648         // This is possible as we called ensureWritable(...) before.
649         for (int i = start; i < end; i++) {
650             char c = seq.charAt(i);
651             if (c < 0x80) {
652                 buffer.put(writerIndex++, (byte) c);
653             } else if (c < 0x800) {
654                 buffer.put(writerIndex++, (byte) (0xc0 | (c >> 6)));
655                 buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
656             } else if (isSurrogate(c)) {
657                 if (!Character.isHighSurrogate(c)) {
658                     buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
659                     continue;
660                 }
661                 // Surrogate Pair consumes 2 characters.
662                 if (++i == end) {
663                     buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
664                     break;
665                 }
666                 // Extra method is copied here to NOT allow inlining of writeUtf8
667                 // and increase the chance to inline CharSequence::charAt instead
668                 char c2 = seq.charAt(i);
669                 if (!Character.isLowSurrogate(c2)) {
670                     buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
671                     buffer.put(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : (byte) c2);
672                 } else {
673                     int codePoint = Character.toCodePoint(c, c2);
674                     // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
675                     buffer.put(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
676                     buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
677                     buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
678                     buffer.put(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
679                 }
680             } else {
681                 buffer.put(writerIndex++, (byte) (0xe0 | (c >> 12)));
682                 buffer.put(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
683                 buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
684             }
685         }
686         return writerIndex - oldWriterIndex;
687     }
688
689     // Safe off-heap Fast-Path implementation
690     private static int safeWriteUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) {
691         assert !(seq instanceof AsciiString);
692         int oldWriterIndex = writerIndex;
693
694         // We can use the _set methods as these not need to do any index checks and reference checks.
695         // This is possible as we called ensureWritable(...) before.
696         for (int i = start; i < end; i++) {
697             char c = seq.charAt(i);
698             if (c < 0x80) {
699                 buffer._setByte(writerIndex++, (byte) c);
700             } else if (c < 0x800) {
701                 buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6)));
702                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
703             } else if (isSurrogate(c)) {
704                 if (!Character.isHighSurrogate(c)) {
705                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
706                     continue;
707                 }
708                 // Surrogate Pair consumes 2 characters.
709                 if (++i == end) {
710                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
711                     break;
712                 }
713                 // Extra method is copied here to NOT allow inlining of writeUtf8
714                 // and increase the chance to inline CharSequence::charAt instead
715                 char c2 = seq.charAt(i);
716                 if (!Character.isLowSurrogate(c2)) {
717                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
718                     buffer._setByte(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
719                 } else {
720                     int codePoint = Character.toCodePoint(c, c2);
721                     // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
722                     buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
723                     buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
724                     buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
725                     buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
726                 }
727             } else {
728                 buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
729                 buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
730                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
731             }
732         }
733         return writerIndex - oldWriterIndex;
734     }
735
736     // safe byte[] Fast-Path implementation
737     private static int safeArrayWriteUtf8(byte[] buffer, int writerIndex, CharSequence seq, int start, int end) {
738         int oldWriterIndex = writerIndex;
739         for (int i = start; i < end; i++) {
740             char c = seq.charAt(i);
741             if (c < 0x80) {
742                 buffer[writerIndex++] = (byte) c;
743             } else if (c < 0x800) {
744                 buffer[writerIndex++] = (byte) (0xc0 | (c >> 6));
745                 buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
746             } else if (isSurrogate(c)) {
747                 if (!Character.isHighSurrogate(c)) {
748                     buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
749                     continue;
750                 }
751                 // Surrogate Pair consumes 2 characters.
752                 if (++i == end) {
753                     buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
754                     break;
755                 }
756                 char c2 = seq.charAt(i);
757                 // Extra method is copied here to NOT allow inlining of writeUtf8
758                 // and increase the chance to inline CharSequence::charAt instead
759                 if (!Character.isLowSurrogate(c2)) {
760                     buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
761                     buffer[writerIndex++] = (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
762                 } else {
763                     int codePoint = Character.toCodePoint(c, c2);
764                     // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
765                     buffer[writerIndex++] = (byte) (0xf0 | (codePoint >> 18));
766                     buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 12) & 0x3f));
767                     buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 6) & 0x3f));
768                     buffer[writerIndex++] = (byte) (0x80 | (codePoint & 0x3f));
769                 }
770             } else {
771                 buffer[writerIndex++] = (byte) (0xe0 | (c >> 12));
772                 buffer[writerIndex++] = (byte) (0x80 | ((c >> 6) & 0x3f));
773                 buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
774             }
775         }
776         return writerIndex - oldWriterIndex;
777     }
778
779     // unsafe Fast-Path implementation
780     private static int unsafeWriteUtf8(byte[] buffer, long memoryOffset, int writerIndex,
781                                        CharSequence seq, int start, int end) {
782         assert !(seq instanceof AsciiString);
783         long writerOffset = memoryOffset + writerIndex;
784         final long oldWriterOffset = writerOffset;
785         for (int i = start; i < end; i++) {
786             char c = seq.charAt(i);
787             if (c < 0x80) {
788                 PlatformDependent.putByte(buffer, writerOffset++, (byte) c);
789             } else if (c < 0x800) {
790                 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xc0 | (c >> 6)));
791                 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
792             } else if (isSurrogate(c)) {
793                 if (!Character.isHighSurrogate(c)) {
794                     PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
795                     continue;
796                 }
797                 // Surrogate Pair consumes 2 characters.
798                 if (++i == end) {
799                     PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
800                     break;
801                 }
802                 char c2 = seq.charAt(i);
803                 // Extra method is copied here to NOT allow inlining of writeUtf8
804                 // and increase the chance to inline CharSequence::charAt instead
805                 if (!Character.isLowSurrogate(c2)) {
806                     PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
807                     PlatformDependent.putByte(buffer, writerOffset++,
808                                               (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2));
809                 } else {
810                     int codePoint = Character.toCodePoint(c, c2);
811                     // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
812                     PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xf0 | (codePoint >> 18)));
813                     PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
814                     PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
815                     PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (codePoint & 0x3f)));
816                 }
817             } else {
818                 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xe0 | (c >> 12)));
819                 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((c >> 6) & 0x3f)));
820                 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
821             }
822         }
823         return (int) (writerOffset - oldWriterOffset);
824     }
825
826     /**
827      * Returns max bytes length of UTF8 character sequence of the given length.
828      */

829     public static int utf8MaxBytes(final int seqLength) {
830         return seqLength * MAX_BYTES_PER_CHAR_UTF8;
831     }
832
833     /**
834      * Returns max bytes length of UTF8 character sequence.
835      * <p>
836      * It behaves like {@link #utf8MaxBytes(int)} applied to {@code seq} {@link CharSequence#length()}.
837      */

838     public static int utf8MaxBytes(CharSequence seq) {
839         return utf8MaxBytes(seq.length());
840     }
841
842     /**
843      * Returns the exact bytes length of UTF8 character sequence.
844      * <p>
845      * This method is producing the exact length according to {@link #writeUtf8(ByteBuf, CharSequence)}.
846      */

847     public static int utf8Bytes(final CharSequence seq) {
848         return utf8ByteCount(seq, 0, seq.length());
849     }
850
851     /**
852      * Equivalent to <code>{@link #utf8Bytes(CharSequence) utf8Bytes(seq.subSequence(start, end))}</code>
853      * but avoids subsequence object allocation.
854      * <p>
855      * This method is producing the exact length according to {@link #writeUtf8(ByteBuf, CharSequence, intint)}.
856      */

857     public static int utf8Bytes(final CharSequence seq, int start, int end) {
858         return utf8ByteCount(checkCharSequenceBounds(seq, start, end), start, end);
859     }
860
861     private static int utf8ByteCount(final CharSequence seq, int start, int end) {
862         if (seq instanceof AsciiString) {
863             return end - start;
864         }
865         int i = start;
866         // ASCII fast path
867         while (i < end && seq.charAt(i) < 0x80) {
868             ++i;
869         }
870         // !ASCII is packed in a separate method to let the ASCII case be smaller
871         return i < end ? (i - start) + utf8BytesNonAscii(seq, i, end) : i - start;
872     }
873
874     private static int utf8BytesNonAscii(final CharSequence seq, final int start, final int end) {
875         int encodedLength = 0;
876         for (int i = start; i < end; i++) {
877             final char c = seq.charAt(i);
878             // making it 100% branchless isn't rewarding due to the many bit operations necessary!
879             if (c < 0x800) {
880                 // branchless version of: (c <= 127 ? 0:1) + 1
881                 encodedLength += ((0x7f - c) >>> 31) + 1;
882             } else if (isSurrogate(c)) {
883                 if (!Character.isHighSurrogate(c)) {
884                     encodedLength++;
885                     // WRITE_UTF_UNKNOWN
886                     continue;
887                 }
888                 // Surrogate Pair consumes 2 characters.
889                 if (++i == end) {
890                     encodedLength++;
891                     // WRITE_UTF_UNKNOWN
892                     break;
893                 }
894                 if (!Character.isLowSurrogate(seq.charAt(i))) {
895                     // WRITE_UTF_UNKNOWN + (Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2)
896                     encodedLength += 2;
897                     continue;
898                 }
899                 // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
900                 encodedLength += 4;
901             } else {
902                 encodedLength += 3;
903             }
904         }
905         return encodedLength;
906     }
907
908     /**
909      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write
910      * it to a {@link ByteBuf} allocated with {@code alloc}.
911      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
912      * @param seq The characters to write into a buffer.
913      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> encoded
914      * result.
915      */

916     public static ByteBuf writeAscii(ByteBufAllocator alloc, CharSequence seq) {
917         // ASCII uses 1 byte per char
918         ByteBuf buf = alloc.buffer(seq.length());
919         writeAscii(buf, seq);
920         return buf;
921     }
922
923     /**
924      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write it
925      * to a {@link ByteBuf}.
926      *
927      * This method returns the actual number of bytes written.
928      */

929     public static int writeAscii(ByteBuf buf, CharSequence seq) {
930         // ASCII uses 1 byte per char
931         for (;;) {
932             if (buf instanceof WrappedCompositeByteBuf) {
933                 // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling.
934                 buf = buf.unwrap();
935             } else if (buf instanceof AbstractByteBuf) {
936                 final int len = seq.length();
937                 AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
938                 byteBuf.ensureWritable0(len);
939                 if (seq instanceof AsciiString) {
940                     writeAsciiString(byteBuf, byteBuf.writerIndex, (AsciiString) seq, 0, len);
941                 } else {
942                     final int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
943                     assert written == len;
944                 }
945                 byteBuf.writerIndex += len;
946                 return len;
947             } else if (buf instanceof WrappedByteBuf) {
948                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
949                 buf = buf.unwrap();
950             } else {
951                 byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII);
952                 buf.writeBytes(bytes);
953                 return bytes.length;
954             }
955         }
956     }
957
958     // Fast-Path implementation
959     static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
960
961         // We can use the _set methods as these not need to do any index checks and reference checks.
962         // This is possible as we called ensureWritable(...) before.
963         for (int i = 0; i < len; i++) {
964             buffer._setByte(writerIndex++, AsciiString.c2b(seq.charAt(i)));
965         }
966         return len;
967     }
968
969     /**
970      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
971      * is allocated via the {@link ByteBufAllocator}.
972      */

973     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
974         return encodeString0(alloc, false, src, charset, 0);
975     }
976
977     /**
978      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
979      * is allocated via the {@link ByteBufAllocator}.
980      *
981      * @param alloc The {@link ByteBufAllocator} to allocate {@link ByteBuf}.
982      * @param src The {@link CharBuffer} to encode.
983      * @param charset The specified {@link Charset}.
984      * @param extraCapacity the extra capacity to alloc except the space for decoding.
985      */

986     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset, int extraCapacity) {
987         return encodeString0(alloc, false, src, charset, extraCapacity);
988     }
989
990     static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset,
991                                  int extraCapacity) {
992         final CharsetEncoder encoder = CharsetUtil.encoder(charset);
993         int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar()) + extraCapacity;
994         boolean release = true;
995         final ByteBuf dst;
996         if (enforceHeap) {
997             dst = alloc.heapBuffer(length);
998         } else {
999             dst = alloc.buffer(length);
1000         }
1001         try {
1002             final ByteBuffer dstBuf = dst.internalNioBuffer(dst.readerIndex(), length);
1003             final int pos = dstBuf.position();
1004             CoderResult cr = encoder.encode(src, dstBuf, true);
1005             if (!cr.isUnderflow()) {
1006                 cr.throwException();
1007             }
1008             cr = encoder.flush(dstBuf);
1009             if (!cr.isUnderflow()) {
1010                 cr.throwException();
1011             }
1012             dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
1013             release = false;
1014             return dst;
1015         } catch (CharacterCodingException x) {
1016             throw new IllegalStateException(x);
1017         } finally {
1018             if (release) {
1019                 dst.release();
1020             }
1021         }
1022     }
1023
1024     @SuppressWarnings("deprecation")
1025     static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) {
1026         if (len == 0) {
1027             return StringUtil.EMPTY_STRING;
1028         }
1029         final byte[] array;
1030         final int offset;
1031
1032         if (src.hasArray()) {
1033             array = src.array();
1034             offset = src.arrayOffset() + readerIndex;
1035         } else {
1036             array = threadLocalTempArray(len);
1037             offset = 0;
1038             src.getBytes(readerIndex, array, 0, len);
1039         }
1040         if (CharsetUtil.US_ASCII.equals(charset)) {
1041             // Fast-path for US-ASCII which is used frequently.
1042             return new String(array, 0, offset, len);
1043         }
1044         return new String(array, offset, len, charset);
1045     }
1046
1047     /**
1048      * Returns a cached thread-local direct buffer, if available.
1049      *
1050      * @return a cached thread-local direct buffer, if available.  {@code null} otherwise.
1051      */

1052     public static ByteBuf threadLocalDirectBuffer() {
1053         if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
1054             return null;
1055         }
1056
1057         if (PlatformDependent.hasUnsafe()) {
1058             return ThreadLocalUnsafeDirectByteBuf.newInstance();
1059         } else {
1060             return ThreadLocalDirectByteBuf.newInstance();
1061         }
1062     }
1063
1064     /**
1065      * Create a copy of the underlying storage from {@code buf} into a byte array.
1066      * The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes.
1067      */

1068     public static byte[] getBytes(ByteBuf buf) {
1069         return getBytes(buf,  buf.readerIndex(), buf.readableBytes());
1070     }
1071
1072     /**
1073      * Create a copy of the underlying storage from {@code buf} into a byte array.
1074      * The copy will start at {@code start} and copy {@code length} bytes.
1075      */

1076     public static byte[] getBytes(ByteBuf buf, int start, int length) {
1077         return getBytes(buf, start, length, true);
1078     }
1079
1080     /**
1081      * Return an array of the underlying storage from {@code buf} into a byte array.
1082      * The copy will start at {@code start} and copy {@code length} bytes.
1083      * If {@code copy} is true a copy will be made of the memory.
1084      * If {@code copy} is false the underlying storage will be shared, if possible.
1085      */

1086     public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
1087         int capacity = buf.capacity();
1088         if (isOutOfBounds(start, length, capacity)) {
1089             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
1090                     + ") <= " + "buf.capacity(" + capacity + ')');
1091         }
1092
1093         if (buf.hasArray()) {
1094             int baseOffset = buf.arrayOffset() + start;
1095             byte[] bytes = buf.array();
1096             if (copy || baseOffset != 0 || length != bytes.length) {
1097                 return Arrays.copyOfRange(bytes, baseOffset, baseOffset + length);
1098             } else {
1099                 return bytes;
1100             }
1101         }
1102
1103         byte[] bytes = PlatformDependent.allocateUninitializedArray(length);
1104         buf.getBytes(start, bytes);
1105         return bytes;
1106     }
1107
1108     /**
1109      * Copies the all content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], intint)}.
1110      *
1111      * @param src the source string to copy
1112      * @param dst the destination buffer
1113      */

1114     public static void copy(AsciiString src, ByteBuf dst) {
1115         copy(src, 0, dst, src.length());
1116     }
1117
1118     /**
1119      * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#setBytes(intbyte[], intint)}.
1120      * Unlike the {@link #copy(AsciiString, ByteBuf)} and {@link #copy(AsciiString, int, ByteBuf, int)} methods,
1121      * this method do not increase a {@code writerIndex} of {@code dst} buffer.
1122      *
1123      * @param src the source string to copy
1124      * @param srcIdx the starting offset of characters to copy
1125      * @param dst the destination buffer
1126      * @param dstIdx the starting offset in the destination buffer
1127      * @param length the number of characters to copy
1128      */

1129     public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
1130         if (isOutOfBounds(srcIdx, length, src.length())) {
1131             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
1132                             + length + ") <= srcLen(" + src.length() + ')');
1133         }
1134
1135         checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
1136     }
1137
1138     /**
1139      * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], intint)}.
1140      *
1141      * @param src the source string to copy
1142      * @param srcIdx the starting offset of characters to copy
1143      * @param dst the destination buffer
1144      * @param length the number of characters to copy
1145      */

1146     public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
1147         if (isOutOfBounds(srcIdx, length, src.length())) {
1148             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
1149                             + length + ") <= srcLen(" + src.length() + ')');
1150         }
1151
1152         checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
1153     }
1154
1155     /**
1156      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans.
1157      */

1158     public static String prettyHexDump(ByteBuf buffer) {
1159         return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
1160     }
1161
1162     /**
1163      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans,
1164      * starting at the given {@code offset} using the given {@code length}.
1165      */

1166     public static String prettyHexDump(ByteBuf buffer, int offset, int length) {
1167         return HexUtil.prettyHexDump(buffer, offset, length);
1168     }
1169
1170     /**
1171      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
1172      * {@link StringBuilder} that is easy to read by humans.
1173      */

1174     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) {
1175         appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes());
1176     }
1177
1178     /**
1179      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
1180      * {@link StringBuilder} that is easy to read by humans, starting at the given {@code offset} using
1181      * the given {@code length}.
1182      */

1183     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
1184         HexUtil.appendPrettyHexDump(dump, buf, offset, length);
1185     }
1186
1187     /* Separate class so that the expensive static initialization is only done when needed */
1188     private static final class HexUtil {
1189
1190         private static final char[] BYTE2CHAR = new char[256];
1191         private static final char[] HEXDUMP_TABLE = new char[256 * 4];
1192         private static final String[] HEXPADDING = new String[16];
1193         private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
1194         private static final String[] BYTE2HEX = new String[256];
1195         private static final String[] BYTEPADDING = new String[16];
1196
1197         static {
1198             final char[] DIGITS = "0123456789abcdef".toCharArray();
1199             for (int i = 0; i < 256; i ++) {
1200                 HEXDUMP_TABLE[ i << 1     ] = DIGITS[i >>> 4 & 0x0F];
1201                 HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i       & 0x0F];
1202             }
1203
1204             int i;
1205
1206             // Generate the lookup table for hex dump paddings
1207             for (i = 0; i < HEXPADDING.length; i ++) {
1208                 int padding = HEXPADDING.length - i;
1209                 StringBuilder buf = new StringBuilder(padding * 3);
1210                 for (int j = 0; j < padding; j ++) {
1211                     buf.append("   ");
1212                 }
1213                 HEXPADDING[i] = buf.toString();
1214             }
1215
1216             // Generate the lookup table for the start-offset header in each row (up to 64KiB).
1217             for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) {
1218                 StringBuilder buf = new StringBuilder(12);
1219                 buf.append(NEWLINE);
1220                 buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
1221                 buf.setCharAt(buf.length() - 9, '|');
1222                 buf.append('|');
1223                 HEXDUMP_ROWPREFIXES[i] = buf.toString();
1224             }
1225
1226             // Generate the lookup table for byte-to-hex-dump conversion
1227             for (i = 0; i < BYTE2HEX.length; i ++) {
1228                 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
1229             }
1230
1231             // Generate the lookup table for byte dump paddings
1232             for (i = 0; i < BYTEPADDING.length; i ++) {
1233                 int padding = BYTEPADDING.length - i;
1234                 StringBuilder buf = new StringBuilder(padding);
1235                 for (int j = 0; j < padding; j ++) {
1236                     buf.append(' ');
1237                 }
1238                 BYTEPADDING[i] = buf.toString();
1239             }
1240
1241             // Generate the lookup table for byte-to-char conversion
1242             for (i = 0; i < BYTE2CHAR.length; i ++) {
1243                 if (i <= 0x1f || i >= 0x7f) {
1244                     BYTE2CHAR[i] = '.';
1245                 } else {
1246                     BYTE2CHAR[i] = (char) i;
1247                 }
1248             }
1249         }
1250
1251         private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
1252             checkPositiveOrZero(length, "length");
1253             if (length == 0) {
1254               return "";
1255             }
1256
1257             int endIndex = fromIndex + length;
1258             char[] buf = new char[length << 1];
1259
1260             int srcIdx = fromIndex;
1261             int dstIdx = 0;
1262             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1263               System.arraycopy(
1264                   HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
1265                   buf, dstIdx, 2);
1266             }
1267
1268             return new String(buf);
1269         }
1270
1271         private static String hexDump(byte[] array, int fromIndex, int length) {
1272             checkPositiveOrZero(length, "length");
1273             if (length == 0) {
1274                 return "";
1275             }
1276
1277             int endIndex = fromIndex + length;
1278             char[] buf = new char[length << 1];
1279
1280             int srcIdx = fromIndex;
1281             int dstIdx = 0;
1282             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1283                 System.arraycopy(
1284                     HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1,
1285                     buf, dstIdx, 2);
1286             }
1287
1288             return new String(buf);
1289         }
1290
1291         private static String prettyHexDump(ByteBuf buffer, int offset, int length) {
1292             if (length == 0) {
1293               return StringUtil.EMPTY_STRING;
1294             } else {
1295                 int rows = length / 16 + ((length & 15) == 0? 0 : 1) + 4;
1296                 StringBuilder buf = new StringBuilder(rows * 80);
1297                 appendPrettyHexDump(buf, buffer, offset, length);
1298                 return buf.toString();
1299             }
1300         }
1301
1302         private static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
1303             if (isOutOfBounds(offset, length, buf.capacity())) {
1304                 throw new IndexOutOfBoundsException(
1305                         "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
1306                                                     + ") <= " + "buf.capacity(" + buf.capacity() + ')');
1307             }
1308             if (length == 0) {
1309                 return;
1310             }
1311             dump.append(
1312                               "         +-------------------------------------------------+" +
1313                     NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
1314                     NEWLINE + "+--------+-------------------------------------------------+----------------+");
1315
1316             final int startIndex = offset;
1317             final int fullRows = length >>> 4;
1318             final int remainder = length & 0xF;
1319
1320             // Dump the rows which have 16 bytes.
1321             for (int row = 0; row < fullRows; row ++) {
1322                 int rowStartIndex = (row << 4) + startIndex;
1323
1324                 // Per-row prefix.
1325                 appendHexDumpRowPrefix(dump, row, rowStartIndex);
1326
1327                 // Hex dump
1328                 int rowEndIndex = rowStartIndex + 16;
1329                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1330                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1331                 }
1332                 dump.append(" |");
1333
1334                 // ASCII dump
1335                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1336                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1337                 }
1338                 dump.append('|');
1339             }
1340
1341             // Dump the last row which has less than 16 bytes.
1342             if (remainder != 0) {
1343                 int rowStartIndex = (fullRows << 4) + startIndex;
1344                 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
1345
1346                 // Hex dump
1347                 int rowEndIndex = rowStartIndex + remainder;
1348                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1349                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1350                 }
1351                 dump.append(HEXPADDING[remainder]);
1352                 dump.append(" |");
1353
1354                 // Ascii dump
1355                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1356                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1357                 }
1358                 dump.append(BYTEPADDING[remainder]);
1359                 dump.append('|');
1360             }
1361
1362             dump.append(NEWLINE +
1363                         "+--------+-------------------------------------------------+----------------+");
1364         }
1365
1366         private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
1367             if (row < HEXDUMP_ROWPREFIXES.length) {
1368                 dump.append(HEXDUMP_ROWPREFIXES[row]);
1369             } else {
1370                 dump.append(NEWLINE);
1371                 dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
1372                 dump.setCharAt(dump.length() - 9, '|');
1373                 dump.append('|');
1374             }
1375         }
1376     }
1377
1378     static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
1379
1380         private static final ObjectPool<ThreadLocalUnsafeDirectByteBuf> RECYCLER =
1381                 ObjectPool.newPool(new ObjectCreator<ThreadLocalUnsafeDirectByteBuf>() {
1382                     @Override
1383                     public ThreadLocalUnsafeDirectByteBuf newObject(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1384                         return new ThreadLocalUnsafeDirectByteBuf(handle);
1385                     }
1386                 });
1387
1388         static ThreadLocalUnsafeDirectByteBuf newInstance() {
1389             ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get();
1390             buf.resetRefCnt();
1391             return buf;
1392         }
1393
1394         private final Handle<ThreadLocalUnsafeDirectByteBuf> handle;
1395
1396         private ThreadLocalUnsafeDirectByteBuf(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1397             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1398             this.handle = handle;
1399         }
1400
1401         @Override
1402         protected void deallocate() {
1403             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1404                 super.deallocate();
1405             } else {
1406                 clear();
1407                 handle.recycle(this);
1408             }
1409         }
1410     }
1411
1412     static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf {
1413
1414         private static final ObjectPool<ThreadLocalDirectByteBuf> RECYCLER = ObjectPool.newPool(
1415                 new ObjectCreator<ThreadLocalDirectByteBuf>() {
1416             @Override
1417             public ThreadLocalDirectByteBuf newObject(Handle<ThreadLocalDirectByteBuf> handle) {
1418                 return new ThreadLocalDirectByteBuf(handle);
1419             }
1420         });
1421
1422         static ThreadLocalDirectByteBuf newInstance() {
1423             ThreadLocalDirectByteBuf buf = RECYCLER.get();
1424             buf.resetRefCnt();
1425             return buf;
1426         }
1427
1428         private final Handle<ThreadLocalDirectByteBuf> handle;
1429
1430         private ThreadLocalDirectByteBuf(Handle<ThreadLocalDirectByteBuf> handle) {
1431             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1432             this.handle = handle;
1433         }
1434
1435         @Override
1436         protected void deallocate() {
1437             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1438                 super.deallocate();
1439             } else {
1440                 clear();
1441                 handle.recycle(this);
1442             }
1443         }
1444     }
1445
1446     /**
1447      * Returns {@code trueif the given {@link ByteBuf} is valid text using the given {@link Charset},
1448      * otherwise return {@code false}.
1449      *
1450      * @param buf The given {@link ByteBuf}.
1451      * @param charset The specified {@link Charset}.
1452      */

1453     public static boolean isText(ByteBuf buf, Charset charset) {
1454         return isText(buf, buf.readerIndex(), buf.readableBytes(), charset);
1455     }
1456
1457     /**
1458      * Returns {@code trueif the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1459      * text using the given {@link Charset}, otherwise return {@code false}.
1460      *
1461      * @param buf The given {@link ByteBuf}.
1462      * @param index The start index of the specified buffer.
1463      * @param length The length of the specified buffer.
1464      * @param charset The specified {@link Charset}.
1465      *
1466      * @throws IndexOutOfBoundsException if {@code index} + {@code length} is greater than {@code buf.readableBytes}
1467      */

1468     public static boolean isText(ByteBuf buf, int index, int length, Charset charset) {
1469         checkNotNull(buf, "buf");
1470         checkNotNull(charset, "charset");
1471         final int maxIndex = buf.readerIndex() + buf.readableBytes();
1472         if (index < 0 || length < 0 || index > maxIndex - length) {
1473             throw new IndexOutOfBoundsException("index: " + index + " length: " + length);
1474         }
1475         if (charset.equals(CharsetUtil.UTF_8)) {
1476             return isUtf8(buf, index, length);
1477         } else if (charset.equals(CharsetUtil.US_ASCII)) {
1478             return isAscii(buf, index, length);
1479         } else {
1480             CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT);
1481             try {
1482                 if (buf.nioBufferCount() == 1) {
1483                     decoder.decode(buf.nioBuffer(index, length));
1484                 } else {
1485                     ByteBuf heapBuffer = buf.alloc().heapBuffer(length);
1486                     try {
1487                         heapBuffer.writeBytes(buf, index, length);
1488                         decoder.decode(heapBuffer.internalNioBuffer(heapBuffer.readerIndex(), length));
1489                     } finally {
1490                         heapBuffer.release();
1491                     }
1492                 }
1493                 return true;
1494             } catch (CharacterCodingException ignore) {
1495                 return false;
1496             }
1497         }
1498     }
1499
1500     /**
1501      * Aborts on a byte which is not a valid ASCII character.
1502      */

1503     private static final ByteProcessor FIND_NON_ASCII = new ByteProcessor() {
1504         @Override
1505         public boolean process(byte value) {
1506             return value >= 0;
1507         }
1508     };
1509
1510     /**
1511      * Returns {@code trueif the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1512      * ASCII text, otherwise return {@code false}.
1513      *
1514      * @param buf    The given {@link ByteBuf}.
1515      * @param index  The start index of the specified buffer.
1516      * @param length The length of the specified buffer.
1517      */

1518     private static boolean isAscii(ByteBuf buf, int index, int length) {
1519         return buf.forEachByte(index, length, FIND_NON_ASCII) == -1;
1520     }
1521
1522     /**
1523      * Returns {@code trueif the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1524      * UTF8 text, otherwise return {@code false}.
1525      *
1526      * @param buf The given {@link ByteBuf}.
1527      * @param index The start index of the specified buffer.
1528      * @param length The length of the specified buffer.
1529      *
1530      * @see
1531      * <a href=http://www.ietf.org/rfc/rfc3629.txt>UTF-8 Definition</a>
1532      *
1533      * <pre>
1534      * 1. Bytes format of UTF-8
1535      *
1536      * The table below summarizes the format of these different octet types.
1537      * The letter x indicates bits available for encoding bits of the character number.
1538      *
1539      * Char. number range  |        UTF-8 octet sequence
1540      *    (hexadecimal)    |              (binary)
1541      * --------------------+---------------------------------------------
1542      * 0000 0000-0000 007F | 0xxxxxxx
1543      * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1544      * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1545      * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1546      * </pre>
1547      *
1548      * <pre>
1549      * 2. Syntax of UTF-8 Byte Sequences
1550      *
1551      * UTF8-octets = *( UTF8-char )
1552      * UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
1553      * UTF8-1      = %x00-7F
1554      * UTF8-2      = %xC2-DF UTF8-tail
1555      * UTF8-3      = %xE0 %xA0-BF UTF8-tail /
1556      *               %xE1-EC 2( UTF8-tail ) /
1557      *               %xED %x80-9F UTF8-tail /
1558      *               %xEE-EF 2( UTF8-tail )
1559      * UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) /
1560      *               %xF1-F3 3( UTF8-tail ) /
1561      *               %xF4 %x80-8F 2( UTF8-tail )
1562      * UTF8-tail   = %x80-BF
1563      * </pre>
1564      */

1565     private static boolean isUtf8(ByteBuf buf, int index, int length) {
1566         final int endIndex = index + length;
1567         while (index < endIndex) {
1568             byte b1 = buf.getByte(index++);
1569             byte b2, b3, b4;
1570             if ((b1 & 0x80) == 0) {
1571                 // 1 byte
1572                 continue;
1573             }
1574             if ((b1 & 0xE0) == 0xC0) {
1575                 // 2 bytes
1576                 //
1577                 // Bit/Byte pattern
1578                 // 110xxxxx    10xxxxxx
1579                 // C2..DF      80..BF
1580                 if (index >= endIndex) { // no enough bytes
1581                     return false;
1582                 }
1583                 b2 = buf.getByte(index++);
1584                 if ((b2 & 0xC0) != 0x80) { // 2nd byte not starts with 10
1585                     return false;
1586                 }
1587                 if ((b1 & 0xFF) < 0xC2) { // out of lower bound
1588                     return false;
1589                 }
1590             } else if ((b1 & 0xF0) == 0xE0) {
1591                 // 3 bytes
1592                 //
1593                 // Bit/Byte pattern
1594                 // 1110xxxx    10xxxxxx    10xxxxxx
1595                 // E0          A0..BF      80..BF
1596                 // E1..EC      80..BF      80..BF
1597                 // ED          80..9F      80..BF
1598                 // E1..EF      80..BF      80..BF
1599                 if (index > endIndex - 2) { // no enough bytes
1600                     return false;
1601                 }
1602                 b2 = buf.getByte(index++);
1603                 b3 = buf.getByte(index++);
1604                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { // 2nd or 3rd bytes not start with 10
1605                     return false;
1606                 }
1607                 if ((b1 & 0x0F) == 0x00 && (b2 & 0xFF) < 0xA0) { // out of lower bound
1608                     return false;
1609                 }
1610                 if ((b1 & 0x0F) == 0x0D && (b2 & 0xFF) > 0x9F) { // out of upper bound
1611                     return false;
1612                 }
1613             } else if ((b1 & 0xF8) == 0xF0) {
1614                 // 4 bytes
1615                 //
1616                 // Bit/Byte pattern
1617                 // 11110xxx    10xxxxxx    10xxxxxx    10xxxxxx
1618                 // F0          90..BF      80..BF      80..BF
1619                 // F1..F3      80..BF      80..BF      80..BF
1620                 // F4          80..8F      80..BF      80..BF
1621                 if (index > endIndex - 3) { // no enough bytes
1622                     return false;
1623                 }
1624                 b2 = buf.getByte(index++);
1625                 b3 = buf.getByte(index++);
1626                 b4 = buf.getByte(index++);
1627                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) {
1628                     // 2nd, 3rd or 4th bytes not start with 10
1629                     return false;
1630                 }
1631                 if ((b1 & 0xFF) > 0xF4 // b1 invalid
1632                         || (b1 & 0xFF) == 0xF0 && (b2 & 0xFF) < 0x90    // b2 out of lower bound
1633                         || (b1 & 0xFF) == 0xF4 && (b2 & 0xFF) > 0x8F) { // b2 out of upper bound
1634                     return false;
1635                 }
1636             } else {
1637                 return false;
1638             }
1639         }
1640         return true;
1641     }
1642
1643     /**
1644      * Read bytes from the given {@link ByteBuffer} into the given {@link OutputStream} using the {@code position} and
1645      * {@code length}. The position and limit of the given {@link ByteBuffer} may be adjusted.
1646      */

1647     static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int position, int length, OutputStream out)
1648             throws IOException {
1649         if (buffer.hasArray()) {
1650             out.write(buffer.array(), position + buffer.arrayOffset(), length);
1651         } else {
1652             int chunkLen = Math.min(length, WRITE_CHUNK_SIZE);
1653             buffer.clear().position(position);
1654
1655             if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) {
1656                 getBytes(buffer, threadLocalTempArray(chunkLen), 0, chunkLen, out, length);
1657             } else {
1658                 // if direct buffers are pooled chances are good that heap buffers are pooled as well.
1659                 ByteBuf tmpBuf = allocator.heapBuffer(chunkLen);
1660                 try {
1661                     byte[] tmp = tmpBuf.array();
1662                     int offset = tmpBuf.arrayOffset();
1663                     getBytes(buffer, tmp, offset, chunkLen, out, length);
1664                 } finally {
1665                     tmpBuf.release();
1666                 }
1667             }
1668         }
1669     }
1670
1671     private static void getBytes(ByteBuffer inBuffer, byte[] in, int inOffset, int inLen, OutputStream out, int outLen)
1672             throws IOException {
1673         do {
1674             int len = Math.min(inLen, outLen);
1675             inBuffer.get(in, inOffset, len);
1676             out.write(in, inOffset, len);
1677             outLen -= len;
1678         } while (outLen > 0);
1679     }
1680
1681     private ByteBufUtil() { }
1682 }
1683