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 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.Recycler;
22  import io.netty.util.Recycler.Handle;
23  import io.netty.util.concurrent.FastThreadLocal;
24  import io.netty.util.internal.PlatformDependent;
25  import io.netty.util.internal.StringUtil;
26  import io.netty.util.internal.SystemPropertyUtil;
27  import io.netty.util.internal.logging.InternalLogger;
28  import io.netty.util.internal.logging.InternalLoggerFactory;
29  
30  import java.io.IOException;
31  import java.io.OutputStream;
32  import java.nio.ByteBuffer;
33  import java.nio.ByteOrder;
34  import java.nio.CharBuffer;
35  import java.nio.charset.CharacterCodingException;
36  import java.nio.charset.Charset;
37  import java.nio.charset.CharsetDecoder;
38  import java.nio.charset.CharsetEncoder;
39  import java.nio.charset.CoderResult;
40  import java.nio.charset.CodingErrorAction;
41  import java.util.Arrays;
42  import java.util.Locale;
43  
44  import static io.netty.util.internal.MathUtil.isOutOfBounds;
45  import static io.netty.util.internal.ObjectUtil.checkNotNull;
46  import static io.netty.util.internal.StringUtil.NEWLINE;
47  import static io.netty.util.internal.StringUtil.isSurrogate;
48  
49  /**
50   * A collection of utility methods that is related with handling {@link ByteBuf},
51   * such as the generation of hex dump and swapping an integer's byte order.
52   */
53  public final class ByteBufUtil {
54  
55      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
56      private static final FastThreadLocal<byte[]> BYTE_ARRAYS = new FastThreadLocal<byte[]>() {
57          @Override
58          protected byte[] initialValue() throws Exception {
59              return PlatformDependent.allocateUninitializedArray(MAX_TL_ARRAY_LEN);
60          }
61      };
62  
63      private static final byte WRITE_UTF_UNKNOWN = (byte) '?';
64      private static final int MAX_CHAR_BUFFER_SIZE;
65      private static final int THREAD_LOCAL_BUFFER_SIZE;
66      private static final int MAX_BYTES_PER_CHAR_UTF8 =
67              (int) CharsetUtil.encoder(CharsetUtil.UTF_8).maxBytesPerChar();
68  
69      static final int WRITE_CHUNK_SIZE = 8192;
70      static final ByteBufAllocator DEFAULT_ALLOCATOR;
71  
72      static {
73          String allocType = SystemPropertyUtil.get(
74                  "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
75          allocType = allocType.toLowerCase(Locale.US).trim();
76  
77          ByteBufAllocator alloc;
78          if ("unpooled".equals(allocType)) {
79              alloc = UnpooledByteBufAllocator.DEFAULT;
80              logger.debug("-Dio.netty.allocator.type: {}", allocType);
81          } else if ("pooled".equals(allocType)) {
82              alloc = PooledByteBufAllocator.DEFAULT;
83              logger.debug("-Dio.netty.allocator.type: {}", allocType);
84          } else {
85              alloc = PooledByteBufAllocator.DEFAULT;
86              logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
87          }
88  
89          DEFAULT_ALLOCATOR = alloc;
90  
91          THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 0);
92          logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
93  
94          MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024);
95          logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
96      }
97  
98      static final int MAX_TL_ARRAY_LEN = 1024;
99  
100     /**
101      * Allocates a new array if minLength > {@link ByteBufUtil#MAX_TL_ARRAY_LEN}
102      */
103     static byte[] threadLocalTempArray(int minLength) {
104         return minLength <= MAX_TL_ARRAY_LEN ? BYTE_ARRAYS.get()
105             : PlatformDependent.allocateUninitializedArray(minLength);
106     }
107 
108     /**
109      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
110      * of the specified buffer's readable bytes.
111      */
112     public static String hexDump(ByteBuf buffer) {
113         return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
114     }
115 
116     /**
117      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
118      * of the specified buffer's sub-region.
119      */
120     public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
121         return HexUtil.hexDump(buffer, fromIndex, length);
122     }
123 
124     /**
125      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
126      * of the specified byte array.
127      */
128     public static String hexDump(byte[] array) {
129         return hexDump(array, 0, array.length);
130     }
131 
132     /**
133      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
134      * of the specified byte array's sub-region.
135      */
136     public static String hexDump(byte[] array, int fromIndex, int length) {
137         return HexUtil.hexDump(array, fromIndex, length);
138     }
139 
140     /**
141      * Decode a 2-digit hex byte from within a string.
142      */
143     public static byte decodeHexByte(CharSequence s, int pos) {
144         return StringUtil.decodeHexByte(s, pos);
145     }
146 
147     /**
148      * Decodes a string generated by {@link #hexDump(byte[])}
149      */
150     public static byte[] decodeHexDump(CharSequence hexDump) {
151         return StringUtil.decodeHexDump(hexDump, 0, hexDump.length());
152     }
153 
154     /**
155      * Decodes part of a string generated by {@link #hexDump(byte[])}
156      */
157     public static byte[] decodeHexDump(CharSequence hexDump, int fromIndex, int length) {
158         return StringUtil.decodeHexDump(hexDump, fromIndex, length);
159     }
160 
161     /**
162      * Used to determine if the return value of {@link ByteBuf#ensureWritable(int, boolean)} means that there is
163      * adequate space and a write operation will succeed.
164      * @param ensureWritableResult The return value from {@link ByteBuf#ensureWritable(int, boolean)}.
165      * @return {@code true} if {@code ensureWritableResult} means that there is adequate space and a write operation
166      * will succeed.
167      */
168     public static boolean ensureWritableSuccess(int ensureWritableResult) {
169         return ensureWritableResult == 0 || ensureWritableResult == 2;
170     }
171 
172     /**
173      * Calculates the hash code of the specified buffer.  This method is
174      * useful when implementing a new buffer type.
175      */
176     public static int hashCode(ByteBuf buffer) {
177         final int aLen = buffer.readableBytes();
178         final int intCount = aLen >>> 2;
179         final int byteCount = aLen & 3;
180 
181         int hashCode = EmptyByteBuf.EMPTY_BYTE_BUF_HASH_CODE;
182         int arrayIndex = buffer.readerIndex();
183         if (buffer.order() == ByteOrder.BIG_ENDIAN) {
184             for (int i = intCount; i > 0; i --) {
185                 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
186                 arrayIndex += 4;
187             }
188         } else {
189             for (int i = intCount; i > 0; i --) {
190                 hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
191                 arrayIndex += 4;
192             }
193         }
194 
195         for (int i = byteCount; i > 0; i --) {
196             hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
197         }
198 
199         if (hashCode == 0) {
200             hashCode = 1;
201         }
202 
203         return hashCode;
204     }
205 
206     /**
207      * Returns the reader index of needle in haystack, or -1 if needle is not in haystack.
208      */
209     public static int indexOf(ByteBuf needle, ByteBuf haystack) {
210         // TODO: maybe use Boyer Moore for efficiency.
211         int attempts = haystack.readableBytes() - needle.readableBytes() + 1;
212         for (int i = 0; i < attempts; i++) {
213             if (equals(needle, needle.readerIndex(),
214                        haystack, haystack.readerIndex() + i,
215                        needle.readableBytes())) {
216                 return haystack.readerIndex() + i;
217             }
218         }
219         return -1;
220     }
221 
222     /**
223      * Returns {@code true} if and only if the two specified buffers are
224      * identical to each other for {@code length} bytes starting at {@code aStartIndex}
225      * index for the {@code a} buffer and {@code bStartIndex} index for the {@code b} buffer.
226      * A more compact way to express this is:
227      * <p>
228      * {@code a[aStartIndex : aStartIndex + length] == b[bStartIndex : bStartIndex + length]}
229      */
230     public static boolean equals(ByteBuf a, int aStartIndex, ByteBuf b, int bStartIndex, int length) {
231         if (aStartIndex < 0 || bStartIndex < 0 || length < 0) {
232             throw new IllegalArgumentException("All indexes and lengths must be non-negative");
233         }
234         if (a.writerIndex() - length < aStartIndex || b.writerIndex() - length < bStartIndex) {
235             return false;
236         }
237 
238         final int longCount = length >>> 3;
239         final int byteCount = length & 7;
240 
241         if (a.order() == b.order()) {
242             for (int i = longCount; i > 0; i --) {
243                 if (a.getLong(aStartIndex) != b.getLong(bStartIndex)) {
244                     return false;
245                 }
246                 aStartIndex += 8;
247                 bStartIndex += 8;
248             }
249         } else {
250             for (int i = longCount; i > 0; i --) {
251                 if (a.getLong(aStartIndex) != swapLong(b.getLong(bStartIndex))) {
252                     return false;
253                 }
254                 aStartIndex += 8;
255                 bStartIndex += 8;
256             }
257         }
258 
259         for (int i = byteCount; i > 0; i --) {
260             if (a.getByte(aStartIndex) != b.getByte(bStartIndex)) {
261                 return false;
262             }
263             aStartIndex ++;
264             bStartIndex ++;
265         }
266 
267         return true;
268     }
269 
270     /**
271      * Returns {@code true} if and only if the two specified buffers are
272      * identical to each other as described in {@link ByteBuf#equals(Object)}.
273      * This method is useful when implementing a new buffer type.
274      */
275     public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
276         final int aLen = bufferA.readableBytes();
277         if (aLen != bufferB.readableBytes()) {
278             return false;
279         }
280         return equals(bufferA, bufferA.readerIndex(), bufferB, bufferB.readerIndex(), aLen);
281     }
282 
283     /**
284      * Compares the two specified buffers as described in {@link ByteBuf#compareTo(ByteBuf)}.
285      * This method is useful when implementing a new buffer type.
286      */
287     public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
288         final int aLen = bufferA.readableBytes();
289         final int bLen = bufferB.readableBytes();
290         final int minLength = Math.min(aLen, bLen);
291         final int uintCount = minLength >>> 2;
292         final int byteCount = minLength & 3;
293         int aIndex = bufferA.readerIndex();
294         int bIndex = bufferB.readerIndex();
295 
296         if (uintCount > 0) {
297             boolean bufferAIsBigEndian = bufferA.order() == ByteOrder.BIG_ENDIAN;
298             final long res;
299             int uintCountIncrement = uintCount << 2;
300 
301             if (bufferA.order() == bufferB.order()) {
302                 res = bufferAIsBigEndian ? compareUintBigEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
303                         compareUintLittleEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
304             } else {
305                 res = bufferAIsBigEndian ? compareUintBigEndianA(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
306                         compareUintBigEndianB(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
307             }
308             if (res != 0) {
309                 // Ensure we not overflow when cast
310                 return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, res));
311             }
312             aIndex += uintCountIncrement;
313             bIndex += uintCountIncrement;
314         }
315 
316         for (int aEnd = aIndex + byteCount; aIndex < aEnd; ++aIndex, ++bIndex) {
317             int comp = bufferA.getUnsignedByte(aIndex) - bufferB.getUnsignedByte(bIndex);
318             if (comp != 0) {
319                 return comp;
320             }
321         }
322 
323         return aLen - bLen;
324     }
325 
326     private static long compareUintBigEndian(
327             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
328         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
329             long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedInt(bIndex);
330             if (comp != 0) {
331                 return comp;
332             }
333         }
334         return 0;
335     }
336 
337     private static long compareUintLittleEndian(
338             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
339         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
340             long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedIntLE(bIndex);
341             if (comp != 0) {
342                 return comp;
343             }
344         }
345         return 0;
346     }
347 
348     private static long compareUintBigEndianA(
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.getUnsignedIntLE(bIndex);
352             if (comp != 0) {
353                 return comp;
354             }
355         }
356         return 0;
357     }
358 
359     private static long compareUintBigEndianB(
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.getUnsignedInt(bIndex);
363             if (comp != 0) {
364                 return comp;
365             }
366         }
367         return 0;
368     }
369 
370     /**
371      * The default implementation of {@link ByteBuf#indexOf(int, int, byte)}.
372      * This method is useful when implementing a new buffer type.
373      */
374     public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
375         if (fromIndex <= toIndex) {
376             return firstIndexOf(buffer, fromIndex, toIndex, value);
377         } else {
378             return lastIndexOf(buffer, fromIndex, toIndex, value);
379         }
380     }
381 
382     /**
383      * Toggles the endianness of the specified 16-bit short integer.
384      */
385     public static short swapShort(short value) {
386         return Short.reverseBytes(value);
387     }
388 
389     /**
390      * Toggles the endianness of the specified 24-bit medium integer.
391      */
392     public static int swapMedium(int value) {
393         int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
394         if ((swapped & 0x800000) != 0) {
395             swapped |= 0xff000000;
396         }
397         return swapped;
398     }
399 
400     /**
401      * Toggles the endianness of the specified 32-bit integer.
402      */
403     public static int swapInt(int value) {
404         return Integer.reverseBytes(value);
405     }
406 
407     /**
408      * Toggles the endianness of the specified 64-bit long integer.
409      */
410     public static long swapLong(long value) {
411         return Long.reverseBytes(value);
412     }
413 
414     /**
415      * Writes a big-endian 16-bit short integer to the buffer.
416      */
417     @SuppressWarnings("deprecation")
418     public static ByteBuf writeShortBE(ByteBuf buf, int shortValue) {
419         return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeShort(shortValue) : buf.writeShortLE(shortValue);
420     }
421 
422     /**
423      * Sets a big-endian 16-bit short integer to the buffer.
424      */
425     @SuppressWarnings("deprecation")
426     public static ByteBuf setShortBE(ByteBuf buf, int index, int shortValue) {
427         return buf.order() == ByteOrder.BIG_ENDIAN? buf.setShort(index, shortValue) : buf.setShortLE(index, shortValue);
428     }
429 
430     /**
431      * Writes a big-endian 24-bit medium integer to the buffer.
432      */
433     @SuppressWarnings("deprecation")
434     public static ByteBuf writeMediumBE(ByteBuf buf, int mediumValue) {
435         return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeMedium(mediumValue) : buf.writeMediumLE(mediumValue);
436     }
437 
438     /**
439      * Read the given amount of bytes into a new {@link ByteBuf} that is allocated from the {@link ByteBufAllocator}.
440      */
441     public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) {
442         boolean release = true;
443         ByteBuf dst = alloc.buffer(length);
444         try {
445             buffer.readBytes(dst);
446             release = false;
447             return dst;
448         } finally {
449             if (release) {
450                 dst.release();
451             }
452         }
453     }
454 
455     private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
456         fromIndex = Math.max(fromIndex, 0);
457         if (fromIndex >= toIndex || buffer.capacity() == 0) {
458             return -1;
459         }
460 
461         return buffer.forEachByte(fromIndex, toIndex - fromIndex, new ByteProcessor.IndexOfProcessor(value));
462     }
463 
464     private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
465         int capacity = buffer.capacity();
466         fromIndex = Math.min(fromIndex, capacity);
467         if (fromIndex < 0 || capacity == 0) {
468             return -1;
469         }
470 
471         return buffer.forEachByteDesc(toIndex, fromIndex - toIndex, new ByteProcessor.IndexOfProcessor(value));
472     }
473 
474     /**
475      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
476      * it to a {@link ByteBuf} allocated with {@code alloc}.
477      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
478      * @param seq The characters to write into a buffer.
479      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoded
480      * result.
481      */
482     public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
483         // UTF-8 uses max. 3 bytes per char, so calculate the worst case.
484         ByteBuf buf = alloc.buffer(utf8MaxBytes(seq));
485         writeUtf8(buf, seq);
486         return buf;
487     }
488 
489     /**
490      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
491      * it to a {@link ByteBuf}.
492      * <p>
493      * It behaves like {@link #reserveAndWriteUtf8(ByteBuf, CharSequence, int)} with {@code reserveBytes}
494      * computed by {@link #utf8MaxBytes(CharSequence)}.<br>
495      * This method returns the actual number of bytes written.
496      */
497     public static int writeUtf8(ByteBuf buf, CharSequence seq) {
498         return reserveAndWriteUtf8(buf, seq, utf8MaxBytes(seq));
499     }
500 
501     /**
502      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
503      * it into {@code reserveBytes} of a {@link ByteBuf}.
504      * <p>
505      * The {@code reserveBytes} must be computed (ie eagerly using {@link #utf8MaxBytes(CharSequence)}
506      * or exactly with {@link #utf8Bytes(CharSequence)}) to ensure this method to not fail: for performance reasons
507      * the index checks will be performed using just {@code reserveBytes}.<br>
508      * This method returns the actual number of bytes written.
509      */
510     public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int reserveBytes) {
511         for (;;) {
512             if (buf instanceof WrappedCompositeByteBuf) {
513                 // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling.
514                 buf = buf.unwrap();
515             } else if (buf instanceof AbstractByteBuf) {
516                 AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
517                 byteBuf.ensureWritable0(reserveBytes);
518                 int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, seq.length());
519                 byteBuf.writerIndex += written;
520                 return written;
521             } else if (buf instanceof WrappedByteBuf) {
522                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
523                 buf = buf.unwrap();
524             } else {
525                 byte[] bytes = seq.toString().getBytes(CharsetUtil.UTF_8);
526                 buf.writeBytes(bytes);
527                 return bytes.length;
528             }
529         }
530     }
531 
532     // Fast-Path implementation
533     static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
534         int oldWriterIndex = writerIndex;
535 
536         // We can use the _set methods as these not need to do any index checks and reference checks.
537         // This is possible as we called ensureWritable(...) before.
538         for (int i = 0; i < len; i++) {
539             char c = seq.charAt(i);
540             if (c < 0x80) {
541                 buffer._setByte(writerIndex++, (byte) c);
542             } else if (c < 0x800) {
543                 buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6)));
544                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
545             } else if (isSurrogate(c)) {
546                 if (!Character.isHighSurrogate(c)) {
547                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
548                     continue;
549                 }
550                 final char c2;
551                 try {
552                     // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid
553                     // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we will
554                     // re-throw a more informative exception describing the problem.
555                     c2 = seq.charAt(++i);
556                 } catch (IndexOutOfBoundsException ignored) {
557                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
558                     break;
559                 }
560                 // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path.
561                 writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, c2);
562             } else {
563                 buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
564                 buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
565                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
566             }
567         }
568         return writerIndex - oldWriterIndex;
569     }
570 
571     private static int writeUtf8Surrogate(AbstractByteBuf buffer, int writerIndex, char c, char c2) {
572         if (!Character.isLowSurrogate(c2)) {
573             buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
574             buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2);
575             return writerIndex;
576         }
577         int codePoint = Character.toCodePoint(c, c2);
578         // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
579         buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
580         buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
581         buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
582         buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
583         return writerIndex;
584     }
585 
586     /**
587      * Returns max bytes length of UTF8 character sequence of the given length.
588      */
589     public static int utf8MaxBytes(final int seqLength) {
590         return seqLength * MAX_BYTES_PER_CHAR_UTF8;
591     }
592 
593     /**
594      * Returns max bytes length of UTF8 character sequence.
595      * <p>
596      * It behaves like {@link #utf8MaxBytes(int)} applied to {@code seq} {@link CharSequence#length()}.
597      */
598     public static int utf8MaxBytes(CharSequence seq) {
599         return utf8MaxBytes(seq.length());
600     }
601 
602     /**
603      * Returns the exact bytes length of UTF8 character sequence.
604      * <p>
605      * This method is producing the exact length according to {@link #writeUtf8(ByteBuf, CharSequence)}.
606      */
607     public static int utf8Bytes(final CharSequence seq) {
608         if (seq instanceof AsciiString) {
609             return seq.length();
610         }
611         int seqLength = seq.length();
612         int i = 0;
613         // ASCII fast path
614         while (i < seqLength && seq.charAt(i) < 0x80) {
615             ++i;
616         }
617         // !ASCII is packed in a separate method to let the ASCII case be smaller
618         return i < seqLength ? i + utf8Bytes(seq, i, seqLength) : i;
619     }
620 
621     private static int utf8Bytes(final CharSequence seq, final int start, final int length) {
622         int encodedLength = 0;
623         for (int i = start; i < length; i++) {
624             final char c = seq.charAt(i);
625             // making it 100% branchless isn't rewarding due to the many bit operations necessary!
626             if (c < 0x800) {
627                 // branchless version of: (c <= 127 ? 0:1) + 1
628                 encodedLength += ((0x7f - c) >>> 31) + 1;
629             } else if (isSurrogate(c)) {
630                 if (!Character.isHighSurrogate(c)) {
631                     encodedLength++;
632                     // WRITE_UTF_UNKNOWN
633                     continue;
634                 }
635                 final char c2;
636                 try {
637                     // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid
638                     // duplicate bounds checking with charAt.
639                     c2 = seq.charAt(++i);
640                 } catch (IndexOutOfBoundsException ignored) {
641                     encodedLength++;
642                     // WRITE_UTF_UNKNOWN
643                     break;
644                 }
645                 if (!Character.isLowSurrogate(c2)) {
646                     // WRITE_UTF_UNKNOWN + (Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2)
647                     encodedLength += 2;
648                     continue;
649                 }
650                 // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
651                 encodedLength += 4;
652             } else {
653                 encodedLength += 3;
654             }
655         }
656         return encodedLength;
657     }
658 
659     /**
660      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write
661      * it to a {@link ByteBuf} allocated with {@code alloc}.
662      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
663      * @param seq The characters to write into a buffer.
664      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> encoded
665      * result.
666      */
667     public static ByteBuf writeAscii(ByteBufAllocator alloc, CharSequence seq) {
668         // ASCII uses 1 byte per char
669         ByteBuf buf = alloc.buffer(seq.length());
670         writeAscii(buf, seq);
671         return buf;
672     }
673 
674     /**
675      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write it
676      * to a {@link ByteBuf}.
677      *
678      * This method returns the actual number of bytes written.
679      */
680     public static int writeAscii(ByteBuf buf, CharSequence seq) {
681         // ASCII uses 1 byte per char
682         final int len = seq.length();
683         if (seq instanceof AsciiString) {
684             AsciiString asciiString = (AsciiString) seq;
685             buf.writeBytes(asciiString.array(), asciiString.arrayOffset(), len);
686         } else {
687             for (;;) {
688                 if (buf instanceof WrappedCompositeByteBuf) {
689                     // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling.
690                     buf = buf.unwrap();
691                 } else if (buf instanceof AbstractByteBuf) {
692                     AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
693                     byteBuf.ensureWritable0(len);
694                     int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
695                     byteBuf.writerIndex += written;
696                     return written;
697                 } else if (buf instanceof WrappedByteBuf) {
698                     // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
699                     buf = buf.unwrap();
700                 } else {
701                     byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII);
702                     buf.writeBytes(bytes);
703                     return bytes.length;
704                 }
705             }
706         }
707         return len;
708     }
709 
710     // Fast-Path implementation
711     static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
712 
713         // We can use the _set methods as these not need to do any index checks and reference checks.
714         // This is possible as we called ensureWritable(...) before.
715         for (int i = 0; i < len; i++) {
716             buffer._setByte(writerIndex++, AsciiString.c2b(seq.charAt(i)));
717         }
718         return len;
719     }
720 
721     /**
722      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
723      * is allocated via the {@link ByteBufAllocator}.
724      */
725     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
726         return encodeString0(alloc, false, src, charset, 0);
727     }
728 
729     /**
730      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
731      * is allocated via the {@link ByteBufAllocator}.
732      *
733      * @param alloc The {@link ByteBufAllocator} to allocate {@link ByteBuf}.
734      * @param src The {@link CharBuffer} to encode.
735      * @param charset The specified {@link Charset}.
736      * @param extraCapacity the extra capacity to alloc except the space for decoding.
737      */
738     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset, int extraCapacity) {
739         return encodeString0(alloc, false, src, charset, extraCapacity);
740     }
741 
742     static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset,
743                                  int extraCapacity) {
744         final CharsetEncoder encoder = CharsetUtil.encoder(charset);
745         int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar()) + extraCapacity;
746         boolean release = true;
747         final ByteBuf dst;
748         if (enforceHeap) {
749             dst = alloc.heapBuffer(length);
750         } else {
751             dst = alloc.buffer(length);
752         }
753         try {
754             final ByteBuffer dstBuf = dst.internalNioBuffer(dst.readerIndex(), length);
755             final int pos = dstBuf.position();
756             CoderResult cr = encoder.encode(src, dstBuf, true);
757             if (!cr.isUnderflow()) {
758                 cr.throwException();
759             }
760             cr = encoder.flush(dstBuf);
761             if (!cr.isUnderflow()) {
762                 cr.throwException();
763             }
764             dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
765             release = false;
766             return dst;
767         } catch (CharacterCodingException x) {
768             throw new IllegalStateException(x);
769         } finally {
770             if (release) {
771                 dst.release();
772             }
773         }
774     }
775 
776     @SuppressWarnings("deprecation")
777     static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) {
778         if (len == 0) {
779             return StringUtil.EMPTY_STRING;
780         }
781         final byte[] array;
782         final int offset;
783 
784         if (src.hasArray()) {
785             array = src.array();
786             offset = src.arrayOffset() + readerIndex;
787         } else {
788             array = threadLocalTempArray(len);
789             offset = 0;
790             src.getBytes(readerIndex, array, 0, len);
791         }
792         if (CharsetUtil.US_ASCII.equals(charset)) {
793             // Fast-path for US-ASCII which is used frequently.
794             return new String(array, 0, offset, len);
795         }
796         return new String(array, offset, len, charset);
797     }
798 
799     /**
800      * Returns a cached thread-local direct buffer, if available.
801      *
802      * @return a cached thread-local direct buffer, if available.  {@code null} otherwise.
803      */
804     public static ByteBuf threadLocalDirectBuffer() {
805         if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
806             return null;
807         }
808 
809         if (PlatformDependent.hasUnsafe()) {
810             return ThreadLocalUnsafeDirectByteBuf.newInstance();
811         } else {
812             return ThreadLocalDirectByteBuf.newInstance();
813         }
814     }
815 
816     /**
817      * Create a copy of the underlying storage from {@code buf} into a byte array.
818      * The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes.
819      */
820     public static byte[] getBytes(ByteBuf buf) {
821         return getBytes(buf,  buf.readerIndex(), buf.readableBytes());
822     }
823 
824     /**
825      * Create a copy of the underlying storage from {@code buf} into a byte array.
826      * The copy will start at {@code start} and copy {@code length} bytes.
827      */
828     public static byte[] getBytes(ByteBuf buf, int start, int length) {
829         return getBytes(buf, start, length, true);
830     }
831 
832     /**
833      * Return an array of the underlying storage from {@code buf} into a byte array.
834      * The copy will start at {@code start} and copy {@code length} bytes.
835      * If {@code copy} is true a copy will be made of the memory.
836      * If {@code copy} is false the underlying storage will be shared, if possible.
837      */
838     public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
839         int capacity = buf.capacity();
840         if (isOutOfBounds(start, length, capacity)) {
841             throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
842                     + ") <= " + "buf.capacity(" + capacity + ')');
843         }
844 
845         if (buf.hasArray()) {
846             if (copy || start != 0 || length != capacity) {
847                 int baseOffset = buf.arrayOffset() + start;
848                 return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length);
849             } else {
850                 return buf.array();
851             }
852         }
853 
854         byte[] v = PlatformDependent.allocateUninitializedArray(length);
855         buf.getBytes(start, v);
856         return v;
857     }
858 
859     /**
860      * Copies the all content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}.
861      *
862      * @param src the source string to copy
863      * @param dst the destination buffer
864      */
865     public static void copy(AsciiString src, ByteBuf dst) {
866         copy(src, 0, dst, src.length());
867     }
868 
869     /**
870      * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#setBytes(int, byte[], int, int)}.
871      * Unlike the {@link #copy(AsciiString, ByteBuf)} and {@link #copy(AsciiString, int, ByteBuf, int)} methods,
872      * this method do not increase a {@code writerIndex} of {@code dst} buffer.
873      *
874      * @param src the source string to copy
875      * @param srcIdx the starting offset of characters to copy
876      * @param dst the destination buffer
877      * @param dstIdx the starting offset in the destination buffer
878      * @param length the number of characters to copy
879      */
880     public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
881         if (isOutOfBounds(srcIdx, length, src.length())) {
882             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
883                             + length + ") <= srcLen(" + src.length() + ')');
884         }
885 
886         checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
887     }
888 
889     /**
890      * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}.
891      *
892      * @param src the source string to copy
893      * @param srcIdx the starting offset of characters to copy
894      * @param dst the destination buffer
895      * @param length the number of characters to copy
896      */
897     public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
898         if (isOutOfBounds(srcIdx, length, src.length())) {
899             throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
900                             + length + ") <= srcLen(" + src.length() + ')');
901         }
902 
903         checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
904     }
905 
906     /**
907      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans.
908      */
909     public static String prettyHexDump(ByteBuf buffer) {
910         return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
911     }
912 
913     /**
914      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans,
915      * starting at the given {@code offset} using the given {@code length}.
916      */
917     public static String prettyHexDump(ByteBuf buffer, int offset, int length) {
918         return HexUtil.prettyHexDump(buffer, offset, length);
919     }
920 
921     /**
922      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
923      * {@link StringBuilder} that is easy to read by humans.
924      */
925     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) {
926         appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes());
927     }
928 
929     /**
930      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
931      * {@link StringBuilder} that is easy to read by humans, starting at the given {@code offset} using
932      * the given {@code length}.
933      */
934     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
935         HexUtil.appendPrettyHexDump(dump, buf, offset, length);
936     }
937 
938     /* Separate class so that the expensive static initialization is only done when needed */
939     private static final class HexUtil {
940 
941         private static final char[] BYTE2CHAR = new char[256];
942         private static final char[] HEXDUMP_TABLE = new char[256 * 4];
943         private static final String[] HEXPADDING = new String[16];
944         private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
945         private static final String[] BYTE2HEX = new String[256];
946         private static final String[] BYTEPADDING = new String[16];
947 
948         static {
949             final char[] DIGITS = "0123456789abcdef".toCharArray();
950             for (int i = 0; i < 256; i ++) {
951                 HEXDUMP_TABLE[ i << 1     ] = DIGITS[i >>> 4 & 0x0F];
952                 HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i       & 0x0F];
953             }
954 
955             int i;
956 
957             // Generate the lookup table for hex dump paddings
958             for (i = 0; i < HEXPADDING.length; i ++) {
959                 int padding = HEXPADDING.length - i;
960                 StringBuilder buf = new StringBuilder(padding * 3);
961                 for (int j = 0; j < padding; j ++) {
962                     buf.append("   ");
963                 }
964                 HEXPADDING[i] = buf.toString();
965             }
966 
967             // Generate the lookup table for the start-offset header in each row (up to 64KiB).
968             for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) {
969                 StringBuilder buf = new StringBuilder(12);
970                 buf.append(NEWLINE);
971                 buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
972                 buf.setCharAt(buf.length() - 9, '|');
973                 buf.append('|');
974                 HEXDUMP_ROWPREFIXES[i] = buf.toString();
975             }
976 
977             // Generate the lookup table for byte-to-hex-dump conversion
978             for (i = 0; i < BYTE2HEX.length; i ++) {
979                 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
980             }
981 
982             // Generate the lookup table for byte dump paddings
983             for (i = 0; i < BYTEPADDING.length; i ++) {
984                 int padding = BYTEPADDING.length - i;
985                 StringBuilder buf = new StringBuilder(padding);
986                 for (int j = 0; j < padding; j ++) {
987                     buf.append(' ');
988                 }
989                 BYTEPADDING[i] = buf.toString();
990             }
991 
992             // Generate the lookup table for byte-to-char conversion
993             for (i = 0; i < BYTE2CHAR.length; i ++) {
994                 if (i <= 0x1f || i >= 0x7f) {
995                     BYTE2CHAR[i] = '.';
996                 } else {
997                     BYTE2CHAR[i] = (char) i;
998                 }
999             }
1000         }
1001 
1002         private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
1003             if (length < 0) {
1004               throw new IllegalArgumentException("length: " + length);
1005             }
1006             if (length == 0) {
1007               return "";
1008             }
1009 
1010             int endIndex = fromIndex + length;
1011             char[] buf = new char[length << 1];
1012 
1013             int srcIdx = fromIndex;
1014             int dstIdx = 0;
1015             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1016               System.arraycopy(
1017                   HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
1018                   buf, dstIdx, 2);
1019             }
1020 
1021             return new String(buf);
1022         }
1023 
1024         private static String hexDump(byte[] array, int fromIndex, int length) {
1025             if (length < 0) {
1026               throw new IllegalArgumentException("length: " + length);
1027             }
1028             if (length == 0) {
1029                 return "";
1030             }
1031 
1032             int endIndex = fromIndex + length;
1033             char[] buf = new char[length << 1];
1034 
1035             int srcIdx = fromIndex;
1036             int dstIdx = 0;
1037             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1038                 System.arraycopy(
1039                     HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1,
1040                     buf, dstIdx, 2);
1041             }
1042 
1043             return new String(buf);
1044         }
1045 
1046         private static String prettyHexDump(ByteBuf buffer, int offset, int length) {
1047             if (length == 0) {
1048               return StringUtil.EMPTY_STRING;
1049             } else {
1050                 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
1051                 StringBuilder buf = new StringBuilder(rows * 80);
1052                 appendPrettyHexDump(buf, buffer, offset, length);
1053                 return buf.toString();
1054             }
1055         }
1056 
1057         private static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
1058             if (isOutOfBounds(offset, length, buf.capacity())) {
1059                 throw new IndexOutOfBoundsException(
1060                         "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
1061                                                     + ") <= " + "buf.capacity(" + buf.capacity() + ')');
1062             }
1063             if (length == 0) {
1064                 return;
1065             }
1066             dump.append(
1067                               "         +-------------------------------------------------+" +
1068                     NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
1069                     NEWLINE + "+--------+-------------------------------------------------+----------------+");
1070 
1071             final int startIndex = offset;
1072             final int fullRows = length >>> 4;
1073             final int remainder = length & 0xF;
1074 
1075             // Dump the rows which have 16 bytes.
1076             for (int row = 0; row < fullRows; row ++) {
1077                 int rowStartIndex = (row << 4) + startIndex;
1078 
1079                 // Per-row prefix.
1080                 appendHexDumpRowPrefix(dump, row, rowStartIndex);
1081 
1082                 // Hex dump
1083                 int rowEndIndex = rowStartIndex + 16;
1084                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1085                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1086                 }
1087                 dump.append(" |");
1088 
1089                 // ASCII dump
1090                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1091                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1092                 }
1093                 dump.append('|');
1094             }
1095 
1096             // Dump the last row which has less than 16 bytes.
1097             if (remainder != 0) {
1098                 int rowStartIndex = (fullRows << 4) + startIndex;
1099                 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
1100 
1101                 // Hex dump
1102                 int rowEndIndex = rowStartIndex + remainder;
1103                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1104                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1105                 }
1106                 dump.append(HEXPADDING[remainder]);
1107                 dump.append(" |");
1108 
1109                 // Ascii dump
1110                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1111                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1112                 }
1113                 dump.append(BYTEPADDING[remainder]);
1114                 dump.append('|');
1115             }
1116 
1117             dump.append(NEWLINE +
1118                         "+--------+-------------------------------------------------+----------------+");
1119         }
1120 
1121         private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
1122             if (row < HEXDUMP_ROWPREFIXES.length) {
1123                 dump.append(HEXDUMP_ROWPREFIXES[row]);
1124             } else {
1125                 dump.append(NEWLINE);
1126                 dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
1127                 dump.setCharAt(dump.length() - 9, '|');
1128                 dump.append('|');
1129             }
1130         }
1131     }
1132 
1133     static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
1134 
1135         private static final Recycler<ThreadLocalUnsafeDirectByteBuf> RECYCLER =
1136                 new Recycler<ThreadLocalUnsafeDirectByteBuf>() {
1137                     @Override
1138                     protected ThreadLocalUnsafeDirectByteBuf newObject(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1139                         return new ThreadLocalUnsafeDirectByteBuf(handle);
1140                     }
1141                 };
1142 
1143         static ThreadLocalUnsafeDirectByteBuf newInstance() {
1144             ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get();
1145             buf.setRefCnt(1);
1146             return buf;
1147         }
1148 
1149         private final Handle<ThreadLocalUnsafeDirectByteBuf> handle;
1150 
1151         private ThreadLocalUnsafeDirectByteBuf(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1152             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1153             this.handle = handle;
1154         }
1155 
1156         @Override
1157         protected void deallocate() {
1158             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1159                 super.deallocate();
1160             } else {
1161                 clear();
1162                 handle.recycle(this);
1163             }
1164         }
1165     }
1166 
1167     static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf {
1168 
1169         private static final Recycler<ThreadLocalDirectByteBuf> RECYCLER = new Recycler<ThreadLocalDirectByteBuf>() {
1170             @Override
1171             protected ThreadLocalDirectByteBuf newObject(Handle<ThreadLocalDirectByteBuf> handle) {
1172                 return new ThreadLocalDirectByteBuf(handle);
1173             }
1174         };
1175 
1176         static ThreadLocalDirectByteBuf newInstance() {
1177             ThreadLocalDirectByteBuf buf = RECYCLER.get();
1178             buf.setRefCnt(1);
1179             return buf;
1180         }
1181 
1182         private final Handle<ThreadLocalDirectByteBuf> handle;
1183 
1184         private ThreadLocalDirectByteBuf(Handle<ThreadLocalDirectByteBuf> handle) {
1185             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1186             this.handle = handle;
1187         }
1188 
1189         @Override
1190         protected void deallocate() {
1191             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1192                 super.deallocate();
1193             } else {
1194                 clear();
1195                 handle.recycle(this);
1196             }
1197         }
1198     }
1199 
1200     /**
1201      * Returns {@code true} if the given {@link ByteBuf} is valid text using the given {@link Charset},
1202      * otherwise return {@code false}.
1203      *
1204      * @param buf The given {@link ByteBuf}.
1205      * @param charset The specified {@link Charset}.
1206      */
1207     public static boolean isText(ByteBuf buf, Charset charset) {
1208         return isText(buf, buf.readerIndex(), buf.readableBytes(), charset);
1209     }
1210 
1211     /**
1212      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1213      * text using the given {@link Charset}, otherwise return {@code false}.
1214      *
1215      * @param buf The given {@link ByteBuf}.
1216      * @param index The start index of the specified buffer.
1217      * @param length The length of the specified buffer.
1218      * @param charset The specified {@link Charset}.
1219      *
1220      * @throws IndexOutOfBoundsException if {@code index} + {@code length} is greater than {@code buf.readableBytes}
1221      */
1222     public static boolean isText(ByteBuf buf, int index, int length, Charset charset) {
1223         checkNotNull(buf, "buf");
1224         checkNotNull(charset, "charset");
1225         final int maxIndex = buf.readerIndex() + buf.readableBytes();
1226         if (index < 0 || length < 0 || index > maxIndex - length) {
1227             throw new IndexOutOfBoundsException("index: " + index + " length: " + length);
1228         }
1229         if (charset.equals(CharsetUtil.UTF_8)) {
1230             return isUtf8(buf, index, length);
1231         } else if (charset.equals(CharsetUtil.US_ASCII)) {
1232             return isAscii(buf, index, length);
1233         } else {
1234             CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT);
1235             try {
1236                 if (buf.nioBufferCount() == 1) {
1237                     decoder.decode(buf.nioBuffer(index, length));
1238                 } else {
1239                     ByteBuf heapBuffer = buf.alloc().heapBuffer(length);
1240                     try {
1241                         heapBuffer.writeBytes(buf, index, length);
1242                         decoder.decode(heapBuffer.internalNioBuffer(heapBuffer.readerIndex(), length));
1243                     } finally {
1244                         heapBuffer.release();
1245                     }
1246                 }
1247                 return true;
1248             } catch (CharacterCodingException ignore) {
1249                 return false;
1250             }
1251         }
1252     }
1253 
1254     /**
1255      * Aborts on a byte which is not a valid ASCII character.
1256      */
1257     private static final ByteProcessor FIND_NON_ASCII = new ByteProcessor() {
1258         @Override
1259         public boolean process(byte value) {
1260             return value >= 0;
1261         }
1262     };
1263 
1264     /**
1265      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1266      * ASCII text, otherwise return {@code false}.
1267      *
1268      * @param buf    The given {@link ByteBuf}.
1269      * @param index  The start index of the specified buffer.
1270      * @param length The length of the specified buffer.
1271      */
1272     private static boolean isAscii(ByteBuf buf, int index, int length) {
1273         return buf.forEachByte(index, length, FIND_NON_ASCII) == -1;
1274     }
1275 
1276     /**
1277      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1278      * UTF8 text, otherwise return {@code false}.
1279      *
1280      * @param buf The given {@link ByteBuf}.
1281      * @param index The start index of the specified buffer.
1282      * @param length The length of the specified buffer.
1283      *
1284      * @see
1285      * <a href=http://www.ietf.org/rfc/rfc3629.txt>UTF-8 Definition</a>
1286      *
1287      * <pre>
1288      * 1. Bytes format of UTF-8
1289      *
1290      * The table below summarizes the format of these different octet types.
1291      * The letter x indicates bits available for encoding bits of the character number.
1292      *
1293      * Char. number range  |        UTF-8 octet sequence
1294      *    (hexadecimal)    |              (binary)
1295      * --------------------+---------------------------------------------
1296      * 0000 0000-0000 007F | 0xxxxxxx
1297      * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1298      * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1299      * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1300      * </pre>
1301      *
1302      * <pre>
1303      * 2. Syntax of UTF-8 Byte Sequences
1304      *
1305      * UTF8-octets = *( UTF8-char )
1306      * UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
1307      * UTF8-1      = %x00-7F
1308      * UTF8-2      = %xC2-DF UTF8-tail
1309      * UTF8-3      = %xE0 %xA0-BF UTF8-tail /
1310      *               %xE1-EC 2( UTF8-tail ) /
1311      *               %xED %x80-9F UTF8-tail /
1312      *               %xEE-EF 2( UTF8-tail )
1313      * UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) /
1314      *               %xF1-F3 3( UTF8-tail ) /
1315      *               %xF4 %x80-8F 2( UTF8-tail )
1316      * UTF8-tail   = %x80-BF
1317      * </pre>
1318      */
1319     private static boolean isUtf8(ByteBuf buf, int index, int length) {
1320         final int endIndex = index + length;
1321         while (index < endIndex) {
1322             byte b1 = buf.getByte(index++);
1323             byte b2, b3, b4;
1324             if ((b1 & 0x80) == 0) {
1325                 // 1 byte
1326                 continue;
1327             }
1328             if ((b1 & 0xE0) == 0xC0) {
1329                 // 2 bytes
1330                 //
1331                 // Bit/Byte pattern
1332                 // 110xxxxx    10xxxxxx
1333                 // C2..DF      80..BF
1334                 if (index >= endIndex) { // no enough bytes
1335                     return false;
1336                 }
1337                 b2 = buf.getByte(index++);
1338                 if ((b2 & 0xC0) != 0x80) { // 2nd byte not starts with 10
1339                     return false;
1340                 }
1341                 if ((b1 & 0xFF) < 0xC2) { // out of lower bound
1342                     return false;
1343                 }
1344             } else if ((b1 & 0xF0) == 0xE0) {
1345                 // 3 bytes
1346                 //
1347                 // Bit/Byte pattern
1348                 // 1110xxxx    10xxxxxx    10xxxxxx
1349                 // E0          A0..BF      80..BF
1350                 // E1..EC      80..BF      80..BF
1351                 // ED          80..9F      80..BF
1352                 // E1..EF      80..BF      80..BF
1353                 if (index > endIndex - 2) { // no enough bytes
1354                     return false;
1355                 }
1356                 b2 = buf.getByte(index++);
1357                 b3 = buf.getByte(index++);
1358                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { // 2nd or 3rd bytes not start with 10
1359                     return false;
1360                 }
1361                 if ((b1 & 0x0F) == 0x00 && (b2 & 0xFF) < 0xA0) { // out of lower bound
1362                     return false;
1363                 }
1364                 if ((b1 & 0x0F) == 0x0D && (b2 & 0xFF) > 0x9F) { // out of upper bound
1365                     return false;
1366                 }
1367             } else if ((b1 & 0xF8) == 0xF0) {
1368                 // 4 bytes
1369                 //
1370                 // Bit/Byte pattern
1371                 // 11110xxx    10xxxxxx    10xxxxxx    10xxxxxx
1372                 // F0          90..BF      80..BF      80..BF
1373                 // F1..F3      80..BF      80..BF      80..BF
1374                 // F4          80..8F      80..BF      80..BF
1375                 if (index > endIndex - 3) { // no enough bytes
1376                     return false;
1377                 }
1378                 b2 = buf.getByte(index++);
1379                 b3 = buf.getByte(index++);
1380                 b4 = buf.getByte(index++);
1381                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) {
1382                     // 2nd, 3rd or 4th bytes not start with 10
1383                     return false;
1384                 }
1385                 if ((b1 & 0xFF) > 0xF4 // b1 invalid
1386                         || (b1 & 0xFF) == 0xF0 && (b2 & 0xFF) < 0x90    // b2 out of lower bound
1387                         || (b1 & 0xFF) == 0xF4 && (b2 & 0xFF) > 0x8F) { // b2 out of upper bound
1388                     return false;
1389                 }
1390             } else {
1391                 return false;
1392             }
1393         }
1394         return true;
1395     }
1396 
1397     /**
1398      * Read bytes from the given {@link ByteBuffer} into the given {@link OutputStream} using the {@code position} and
1399      * {@code length}. The position and limit of the given {@link ByteBuffer} may be adjusted.
1400      */
1401     static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int position, int length, OutputStream out)
1402             throws IOException {
1403         if (buffer.hasArray()) {
1404             out.write(buffer.array(), position + buffer.arrayOffset(), length);
1405         } else {
1406             int chunkLen = Math.min(length, WRITE_CHUNK_SIZE);
1407             buffer.clear().position(position);
1408 
1409             if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) {
1410                 getBytes(buffer, threadLocalTempArray(chunkLen), 0, chunkLen, out, length);
1411             } else {
1412                 // if direct buffers are pooled chances are good that heap buffers are pooled as well.
1413                 ByteBuf tmpBuf = allocator.heapBuffer(chunkLen);
1414                 try {
1415                     byte[] tmp = tmpBuf.array();
1416                     int offset = tmpBuf.arrayOffset();
1417                     getBytes(buffer, tmp, offset, chunkLen, out, length);
1418                 } finally {
1419                     tmpBuf.release();
1420                 }
1421             }
1422         }
1423     }
1424 
1425     private static void getBytes(ByteBuffer inBuffer, byte[] in, int inOffset, int inLen, OutputStream out, int outLen)
1426             throws IOException {
1427         do {
1428             int len = Math.min(inLen, outLen);
1429             inBuffer.get(in, inOffset, len);
1430             out.write(in, inOffset, len);
1431             outLen -= len;
1432         } while (outLen > 0);
1433     }
1434 
1435     private ByteBufUtil() { }
1436 }