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.CharsetUtil;
19  import io.netty.util.Recycler;
20  import io.netty.util.Recycler.Handle;
21  import io.netty.util.concurrent.FastThreadLocal;
22  import io.netty.util.internal.PlatformDependent;
23  import io.netty.util.internal.StringUtil;
24  import io.netty.util.internal.SystemPropertyUtil;
25  import io.netty.util.internal.logging.InternalLogger;
26  import io.netty.util.internal.logging.InternalLoggerFactory;
27  
28  import java.nio.ByteBuffer;
29  import java.nio.ByteOrder;
30  import java.nio.CharBuffer;
31  import java.nio.charset.CharacterCodingException;
32  import java.nio.charset.Charset;
33  import java.nio.charset.CharsetDecoder;
34  import java.nio.charset.CharsetEncoder;
35  import java.nio.charset.CoderResult;
36  import java.nio.charset.CodingErrorAction;
37  import java.util.Locale;
38  
39  import static io.netty.util.internal.MathUtil.isOutOfBounds;
40  import static io.netty.util.internal.ObjectUtil.checkNotNull;
41  import static io.netty.util.internal.StringUtil.NEWLINE;
42  import static io.netty.util.internal.StringUtil.isSurrogate;
43  
44  /**
45   * A collection of utility methods that is related with handling {@link ByteBuf},
46   * such as the generation of hex dump and swapping an integer's byte order.
47   */
48  public final class ByteBufUtil {
49  
50      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
51      private static final FastThreadLocal<CharBuffer> CHAR_BUFFERS = new FastThreadLocal<CharBuffer>() {
52          @Override
53          protected CharBuffer initialValue() throws Exception {
54              return CharBuffer.allocate(1024);
55          }
56      };
57  
58      private static final byte WRITE_UTF_UNKNOWN = (byte) '?';
59      private static final int MAX_CHAR_BUFFER_SIZE;
60      private static final int THREAD_LOCAL_BUFFER_SIZE;
61      private static final int MAX_BYTES_PER_CHAR_UTF8 =
62              (int) CharsetUtil.encoder(CharsetUtil.UTF_8).maxBytesPerChar();
63  
64      static final ByteBufAllocator DEFAULT_ALLOCATOR;
65  
66      static {
67          String allocType = SystemPropertyUtil.get("io.netty.allocator.type", "unpooled").toLowerCase(Locale.US).trim();
68          ByteBufAllocator alloc;
69          if ("unpooled".equals(allocType)) {
70              alloc = UnpooledByteBufAllocator.DEFAULT;
71              logger.debug("-Dio.netty.allocator.type: {}", allocType);
72          } else if ("pooled".equals(allocType)) {
73              alloc = PooledByteBufAllocator.DEFAULT;
74              logger.debug("-Dio.netty.allocator.type: {}", allocType);
75          } else {
76              alloc = UnpooledByteBufAllocator.DEFAULT;
77              logger.debug("-Dio.netty.allocator.type: unpooled (unknown: {})", allocType);
78          }
79  
80          DEFAULT_ALLOCATOR = alloc;
81  
82          THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 64 * 1024);
83          logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
84  
85          MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024);
86          logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
87      }
88  
89      /**
90       * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
91       * of the specified buffer's readable bytes.
92       */
93      public static String hexDump(ByteBuf buffer) {
94          return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
95      }
96  
97      /**
98       * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
99       * of the specified buffer's sub-region.
100      */
101     public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
102         return HexUtil.hexDump(buffer, fromIndex, length);
103     }
104 
105     /**
106      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
107      * of the specified byte array.
108      */
109     public static String hexDump(byte[] array) {
110         return hexDump(array, 0, array.length);
111     }
112 
113     /**
114      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
115      * of the specified byte array's sub-region.
116      */
117     public static String hexDump(byte[] array, int fromIndex, int length) {
118         return HexUtil.hexDump(array, fromIndex, length);
119     }
120 
121     /**
122      * Decode a 2-digit hex byte from within a string.
123      */
124     public static byte decodeHexByte(CharSequence s, int pos) {
125         return StringUtil.decodeHexByte(s, pos);
126     }
127 
128     /**
129      * Decodes a string generated by {@link #hexDump(byte[])}
130      */
131     public static byte[] decodeHexDump(CharSequence hexDump) {
132         return StringUtil.decodeHexDump(hexDump, 0, hexDump.length());
133     }
134 
135     /**
136      * Decodes part of a string generated by {@link #hexDump(byte[])}
137      */
138     public static byte[] decodeHexDump(CharSequence hexDump, int fromIndex, int length) {
139         return StringUtil.decodeHexDump(hexDump, fromIndex, length);
140     }
141 
142     /**
143      * Calculates the hash code of the specified buffer.  This method is
144      * useful when implementing a new buffer type.
145      */
146     public static int hashCode(ByteBuf buffer) {
147         final int aLen = buffer.readableBytes();
148         final int intCount = aLen >>> 2;
149         final int byteCount = aLen & 3;
150 
151         int hashCode = 1;
152         int arrayIndex = buffer.readerIndex();
153         if (buffer.order() == ByteOrder.BIG_ENDIAN) {
154             for (int i = intCount; i > 0; i --) {
155                 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
156                 arrayIndex += 4;
157             }
158         } else {
159             for (int i = intCount; i > 0; i --) {
160                 hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
161                 arrayIndex += 4;
162             }
163         }
164 
165         for (int i = byteCount; i > 0; i --) {
166             hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
167         }
168 
169         if (hashCode == 0) {
170             hashCode = 1;
171         }
172 
173         return hashCode;
174     }
175 
176     /**
177      * Returns {@code true} if and only if the two specified buffers are
178      * identical to each other as described in {@code ChannelBuffer#equals(Object)}.
179      * This method is useful when implementing a new buffer type.
180      */
181     public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
182         final int aLen = bufferA.readableBytes();
183         if (aLen != bufferB.readableBytes()) {
184             return false;
185         }
186 
187         final int longCount = aLen >>> 3;
188         final int byteCount = aLen & 7;
189 
190         int aIndex = bufferA.readerIndex();
191         int bIndex = bufferB.readerIndex();
192 
193         if (bufferA.order() == bufferB.order()) {
194             for (int i = longCount; i > 0; i --) {
195                 if (bufferA.getLong(aIndex) != bufferB.getLong(bIndex)) {
196                     return false;
197                 }
198                 aIndex += 8;
199                 bIndex += 8;
200             }
201         } else {
202             for (int i = longCount; i > 0; i --) {
203                 if (bufferA.getLong(aIndex) != swapLong(bufferB.getLong(bIndex))) {
204                     return false;
205                 }
206                 aIndex += 8;
207                 bIndex += 8;
208             }
209         }
210 
211         for (int i = byteCount; i > 0; i --) {
212             if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) {
213                 return false;
214             }
215             aIndex ++;
216             bIndex ++;
217         }
218 
219         return true;
220     }
221 
222     /**
223      * Compares the two specified buffers as described in {@link ByteBuf#compareTo(ByteBuf)}.
224      * This method is useful when implementing a new buffer type.
225      */
226     public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
227         final int aLen = bufferA.readableBytes();
228         final int bLen = bufferB.readableBytes();
229         final int minLength = Math.min(aLen, bLen);
230         final int uintCount = minLength >>> 2;
231         final int byteCount = minLength & 3;
232         int aIndex = bufferA.readerIndex();
233         int bIndex = bufferB.readerIndex();
234 
235         if (uintCount > 0) {
236             boolean bufferAIsBigEndian = bufferA.order() == ByteOrder.BIG_ENDIAN;
237             final long res;
238             int uintCountIncrement = uintCount << 2;
239 
240             if (bufferA.order() == bufferB.order()) {
241                 res = bufferAIsBigEndian ? compareUintBigEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
242                         compareUintLittleEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
243             } else {
244                 res = bufferAIsBigEndian ? compareUintBigEndianA(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
245                         compareUintBigEndianB(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
246             }
247             if (res != 0) {
248                 // Ensure we not overflow when cast
249                 return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, res));
250             }
251             aIndex += uintCountIncrement;
252             bIndex += uintCountIncrement;
253         }
254 
255         for (int aEnd = aIndex + byteCount; aIndex < aEnd; ++aIndex, ++bIndex) {
256             int comp = bufferA.getUnsignedByte(aIndex) - bufferB.getUnsignedByte(bIndex);
257             if (comp != 0) {
258                 return comp;
259             }
260         }
261 
262         return aLen - bLen;
263     }
264 
265     private static long compareUintBigEndian(
266             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
267         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
268             long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedInt(bIndex);
269             if (comp != 0) {
270                 return comp;
271             }
272         }
273         return 0;
274     }
275 
276     private static long compareUintLittleEndian(
277             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
278         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
279             long comp = (swapInt(bufferA.getInt(aIndex)) & 0xFFFFFFFFL) -
280                     (swapInt(bufferB.getInt(bIndex)) & 0xFFFFFFFFL);
281             if (comp != 0) {
282                 return comp;
283             }
284         }
285         return 0;
286     }
287 
288     private static long compareUintBigEndianA(
289             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
290         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
291             long comp =  bufferA.getUnsignedInt(aIndex) - (swapInt(bufferB.getInt(bIndex)) & 0xFFFFFFFFL);
292             if (comp != 0) {
293                 return comp;
294             }
295         }
296         return 0;
297     }
298 
299     private static long compareUintBigEndianB(
300             ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
301         for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
302             long comp =  (swapInt(bufferA.getInt(aIndex)) & 0xFFFFFFFFL) - bufferB.getUnsignedInt(bIndex);
303             if (comp != 0) {
304                 return comp;
305             }
306         }
307         return 0;
308     }
309 
310     /**
311      * The default implementation of {@link ByteBuf#indexOf(int, int, byte)}.
312      * This method is useful when implementing a new buffer type.
313      */
314     public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
315         if (fromIndex <= toIndex) {
316             return firstIndexOf(buffer, fromIndex, toIndex, value);
317         } else {
318             return lastIndexOf(buffer, fromIndex, toIndex, value);
319         }
320     }
321 
322     /**
323      * Toggles the endianness of the specified 16-bit short integer.
324      */
325     public static short swapShort(short value) {
326         return Short.reverseBytes(value);
327     }
328 
329     /**
330      * Toggles the endianness of the specified 24-bit medium integer.
331      */
332     public static int swapMedium(int value) {
333         int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
334         if ((swapped & 0x800000) != 0) {
335             swapped |= 0xff000000;
336         }
337         return swapped;
338     }
339 
340     /**
341      * Toggles the endianness of the specified 32-bit integer.
342      */
343     public static int swapInt(int value) {
344         return Integer.reverseBytes(value);
345     }
346 
347     /**
348      * Toggles the endianness of the specified 64-bit long integer.
349      */
350     public static long swapLong(long value) {
351         return Long.reverseBytes(value);
352     }
353 
354     /**
355      * Read the given amount of bytes into a new {@link ByteBuf} that is allocated from the {@link ByteBufAllocator}.
356      */
357     public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) {
358         boolean release = true;
359         ByteBuf dst = alloc.buffer(length);
360         try {
361             buffer.readBytes(dst);
362             release = false;
363             return dst;
364         } finally {
365             if (release) {
366                 dst.release();
367             }
368         }
369     }
370 
371     private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
372         fromIndex = Math.max(fromIndex, 0);
373         if (fromIndex >= toIndex || buffer.capacity() == 0) {
374             return -1;
375         }
376 
377         return buffer.forEachByte(fromIndex, toIndex - fromIndex, new IndexOfProcessor(value));
378     }
379 
380     private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
381         fromIndex = Math.min(fromIndex, buffer.capacity());
382         if (fromIndex < 0 || buffer.capacity() == 0) {
383             return -1;
384         }
385 
386         return buffer.forEachByteDesc(toIndex, fromIndex - toIndex, new IndexOfProcessor(value));
387     }
388 
389     /**
390      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
391      * it to a {@link ByteBuf} allocated with {@code alloc}.
392      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
393      * @param seq The characters to write into a buffer.
394      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoded
395      * result.
396      */
397     public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
398         // UTF-8 uses max. 3 bytes per char, so calculate the worst case.
399         ByteBuf buf = alloc.buffer(seq.length() * MAX_BYTES_PER_CHAR_UTF8);
400         writeUtf8(buf, seq);
401         return buf;
402     }
403 
404     /**
405      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
406      * it to a {@link ByteBuf}.
407      *
408      * This method returns the actual number of bytes written.
409      */
410     public static int writeUtf8(ByteBuf buf, CharSequence seq) {
411         final int len = seq.length();
412         buf.ensureWritable(len * MAX_BYTES_PER_CHAR_UTF8);
413 
414         for (;;) {
415             if (buf instanceof AbstractByteBuf) {
416                 return writeUtf8((AbstractByteBuf) buf, seq, len);
417             } else if (buf instanceof WrappedByteBuf) {
418                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
419                 buf = buf.unwrap();
420             } else {
421                 byte[] bytes = seq.toString().getBytes(CharsetUtil.UTF_8);
422                 buf.writeBytes(bytes);
423                 return bytes.length;
424             }
425         }
426     }
427 
428     // Fast-Path implementation
429     private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len) {
430         int oldWriterIndex = buffer.writerIndex;
431         int writerIndex = oldWriterIndex;
432 
433         // We can use the _set methods as these not need to do any index checks and reference checks.
434         // This is possible as we called ensureWritable(...) before.
435         for (int i = 0; i < len; i++) {
436             char c = seq.charAt(i);
437             if (c < 0x80) {
438                 buffer._setByte(writerIndex++, (byte) c);
439             } else if (c < 0x800) {
440                 buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6)));
441                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
442             } else if (isSurrogate(c)) {
443                 if (!Character.isHighSurrogate(c)) {
444                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
445                     continue;
446                 }
447                 final char c2;
448                 try {
449                     // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid
450                     // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we will
451                     // re-throw a more informative exception describing the problem.
452                     c2 = seq.charAt(++i);
453                 } catch (IndexOutOfBoundsException e) {
454                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
455                     break;
456                 }
457                 if (!Character.isLowSurrogate(c2)) {
458                     buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
459                     buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2);
460                     continue;
461                 }
462                 int codePoint = Character.toCodePoint(c, c2);
463                 // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
464                 buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
465                 buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
466                 buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
467                 buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
468             } else {
469                 buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
470                 buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
471                 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
472             }
473         }
474         // update the writerIndex without any extra checks for performance reasons
475         buffer.writerIndex = writerIndex;
476         return writerIndex - oldWriterIndex;
477     }
478 
479     /**
480      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write
481      * it to a {@link ByteBuf} allocated with {@code alloc}.
482      * @param alloc The allocator used to allocate a new {@link ByteBuf}.
483      * @param seq The characters to write into a buffer.
484      * @return The {@link ByteBuf} which contains the <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> encoded
485      * result.
486      */
487     public static ByteBuf writeAscii(ByteBufAllocator alloc, CharSequence seq) {
488         // ASCII uses 1 byte per char
489         ByteBuf buf = alloc.buffer(seq.length());
490         writeAscii(buf, seq);
491         return buf;
492     }
493 
494     /**
495      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write it
496      * to a {@link ByteBuf}.
497      *
498      * This method returns the actual number of bytes written.
499      */
500     public static int writeAscii(ByteBuf buf, CharSequence seq) {
501         // ASCII uses 1 byte per char
502         final int len = seq.length();
503         buf.ensureWritable(len);
504         for (;;) {
505             if (buf instanceof AbstractByteBuf) {
506                 writeAscii((AbstractByteBuf) buf, seq, len);
507                 break;
508             } else if (buf instanceof WrappedByteBuf) {
509                 // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
510                 buf = buf.unwrap();
511             } else {
512                 byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII);
513                 buf.writeBytes(bytes);
514                 return bytes.length;
515             }
516         }
517         return len;
518     }
519 
520     // Fast-Path implementation
521     private static void writeAscii(AbstractByteBuf buffer, CharSequence seq, int len) {
522         int writerIndex = buffer.writerIndex;
523 
524         // We can use the _set methods as these not need to do any index checks and reference checks.
525         // This is possible as we called ensureWritable(...) before.
526         for (int i = 0; i < len; i++) {
527             buffer._setByte(writerIndex++, (byte) seq.charAt(i));
528         }
529         // update the writerIndex without any extra checks for performance reasons
530         buffer.writerIndex = writerIndex;
531     }
532 
533     /**
534      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
535      * is allocated via the {@link ByteBufAllocator}.
536      */
537     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
538         return encodeString0(alloc, false, src, charset);
539     }
540 
541     static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset) {
542         final CharsetEncoder encoder = CharsetUtil.encoder(charset);
543         int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar());
544         boolean release = true;
545         final ByteBuf dst;
546         if (enforceHeap) {
547             dst = alloc.heapBuffer(length);
548         } else {
549             dst = alloc.buffer(length);
550         }
551         try {
552             final ByteBuffer dstBuf = dst.internalNioBuffer(dst.readerIndex(), length);
553             final int pos = dstBuf.position();
554             CoderResult cr = encoder.encode(src, dstBuf, true);
555             if (!cr.isUnderflow()) {
556                 cr.throwException();
557             }
558             cr = encoder.flush(dstBuf);
559             if (!cr.isUnderflow()) {
560                 cr.throwException();
561             }
562             dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
563             release = false;
564             return dst;
565         } catch (CharacterCodingException x) {
566             throw new IllegalStateException(x);
567         } finally {
568             if (release) {
569                 dst.release();
570             }
571         }
572     }
573 
574     static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) {
575         if (len == 0) {
576             return StringUtil.EMPTY_STRING;
577         }
578         final CharsetDecoder decoder = CharsetUtil.decoder(charset);
579         final int maxLength = (int) ((double) len * decoder.maxCharsPerByte());
580         CharBuffer dst = CHAR_BUFFERS.get();
581         if (dst.length() < maxLength) {
582             dst = CharBuffer.allocate(maxLength);
583             if (maxLength <= MAX_CHAR_BUFFER_SIZE) {
584                 CHAR_BUFFERS.set(dst);
585             }
586         } else {
587             dst.clear();
588         }
589         if (src.nioBufferCount() == 1) {
590             decodeString(decoder, src.nioBuffer(readerIndex, len), dst);
591         } else {
592             // We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers
593             // are both backed by a byte array.
594             ByteBuf buffer = src.alloc().heapBuffer(len);
595             try {
596                 buffer.writeBytes(src, readerIndex, len);
597                 // Use internalNioBuffer(...) to reduce object creation.
598                 decodeString(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst);
599             } finally {
600                 // Release the temporary buffer again.
601                 buffer.release();
602             }
603         }
604         return dst.flip().toString();
605     }
606 
607     private static void decodeString(CharsetDecoder decoder, ByteBuffer src, CharBuffer dst) {
608         try {
609             CoderResult cr = decoder.decode(src, dst, true);
610             if (!cr.isUnderflow()) {
611                 cr.throwException();
612             }
613             cr = decoder.flush(dst);
614             if (!cr.isUnderflow()) {
615                 cr.throwException();
616             }
617         } catch (CharacterCodingException x) {
618             throw new IllegalStateException(x);
619         }
620     }
621 
622     /**
623      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans.
624      */
625     public static String prettyHexDump(ByteBuf buffer) {
626         return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
627     }
628 
629     /**
630      * Returns a multi-line hexadecimal dump of the specified {@link ByteBuf} that is easy to read by humans,
631      * starting at the given {@code offset} using the given {@code length}.
632      */
633     public static String prettyHexDump(ByteBuf buffer, int offset, int length) {
634         return HexUtil.prettyHexDump(buffer, offset, length);
635     }
636 
637     /**
638      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
639      * {@link StringBuilder} that is easy to read by humans.
640      */
641     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) {
642         appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes());
643     }
644 
645     /**
646      * Appends the prettified multi-line hexadecimal dump of the specified {@link ByteBuf} to the specified
647      * {@link StringBuilder} that is easy to read by humans, starting at the given {@code offset} using
648      * the given {@code length}.
649      */
650     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
651         HexUtil.appendPrettyHexDump(dump, buf, offset, length);
652     }
653 
654     /* Separate class so that the expensive static initialization is only done when needed */
655     private static final class HexUtil {
656 
657         private static final char[] BYTE2CHAR = new char[256];
658         private static final char[] HEXDUMP_TABLE = new char[256 * 4];
659         private static final String[] HEXPADDING = new String[16];
660         private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
661         private static final String[] BYTE2HEX = new String[256];
662         private static final String[] BYTEPADDING = new String[16];
663 
664         static {
665             final char[] DIGITS = "0123456789abcdef".toCharArray();
666             for (int i = 0; i < 256; i ++) {
667                 HEXDUMP_TABLE[ i << 1     ] = DIGITS[i >>> 4 & 0x0F];
668                 HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i       & 0x0F];
669             }
670 
671             int i;
672 
673             // Generate the lookup table for hex dump paddings
674             for (i = 0; i < HEXPADDING.length; i ++) {
675                 int padding = HEXPADDING.length - i;
676                 StringBuilder buf = new StringBuilder(padding * 3);
677                 for (int j = 0; j < padding; j ++) {
678                     buf.append("   ");
679                 }
680                 HEXPADDING[i] = buf.toString();
681             }
682 
683             // Generate the lookup table for the start-offset header in each row (up to 64KiB).
684             for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) {
685                 StringBuilder buf = new StringBuilder(12);
686                 buf.append(NEWLINE);
687                 buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
688                 buf.setCharAt(buf.length() - 9, '|');
689                 buf.append('|');
690                 HEXDUMP_ROWPREFIXES[i] = buf.toString();
691             }
692 
693             // Generate the lookup table for byte-to-hex-dump conversion
694             for (i = 0; i < BYTE2HEX.length; i ++) {
695                 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
696             }
697 
698             // Generate the lookup table for byte dump paddings
699             for (i = 0; i < BYTEPADDING.length; i ++) {
700                 int padding = BYTEPADDING.length - i;
701                 StringBuilder buf = new StringBuilder(padding);
702                 for (int j = 0; j < padding; j ++) {
703                     buf.append(' ');
704                 }
705                 BYTEPADDING[i] = buf.toString();
706             }
707 
708             // Generate the lookup table for byte-to-char conversion
709             for (i = 0; i < BYTE2CHAR.length; i ++) {
710                 if (i <= 0x1f || i >= 0x7f) {
711                     BYTE2CHAR[i] = '.';
712                 } else {
713                     BYTE2CHAR[i] = (char) i;
714                 }
715             }
716         }
717 
718         private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
719             if (length < 0) {
720               throw new IllegalArgumentException("length: " + length);
721             }
722             if (length == 0) {
723               return "";
724             }
725 
726             int endIndex = fromIndex + length;
727             char[] buf = new char[length << 1];
728 
729             int srcIdx = fromIndex;
730             int dstIdx = 0;
731             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
732               System.arraycopy(
733                   HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
734                   buf, dstIdx, 2);
735             }
736 
737             return new String(buf);
738         }
739 
740         private static String hexDump(byte[] array, int fromIndex, int length) {
741             if (length < 0) {
742               throw new IllegalArgumentException("length: " + length);
743             }
744             if (length == 0) {
745                 return "";
746             }
747 
748             int endIndex = fromIndex + length;
749             char[] buf = new char[length << 1];
750 
751             int srcIdx = fromIndex;
752             int dstIdx = 0;
753             for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
754                 System.arraycopy(
755                     HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1,
756                     buf, dstIdx, 2);
757             }
758 
759             return new String(buf);
760         }
761 
762         private static String prettyHexDump(ByteBuf buffer, int offset, int length) {
763             if (length == 0) {
764               return StringUtil.EMPTY_STRING;
765             } else {
766                 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
767                 StringBuilder buf = new StringBuilder(rows * 80);
768                 appendPrettyHexDump(buf, buffer, offset, length);
769                 return buf.toString();
770             }
771         }
772 
773         private static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
774             if (isOutOfBounds(offset, length, buf.capacity())) {
775                 throw new IndexOutOfBoundsException(
776                         "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
777                                                     + ") <= " + "buf.capacity(" + buf.capacity() + ')');
778             }
779             if (length == 0) {
780                 return;
781             }
782             dump.append(
783                               "         +-------------------------------------------------+" +
784                     NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
785                     NEWLINE + "+--------+-------------------------------------------------+----------------+");
786 
787             final int startIndex = offset;
788             final int fullRows = length >>> 4;
789             final int remainder = length & 0xF;
790 
791             // Dump the rows which have 16 bytes.
792             for (int row = 0; row < fullRows; row ++) {
793                 int rowStartIndex = (row << 4) + startIndex;
794 
795                 // Per-row prefix.
796                 appendHexDumpRowPrefix(dump, row, rowStartIndex);
797 
798                 // Hex dump
799                 int rowEndIndex = rowStartIndex + 16;
800                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
801                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
802                 }
803                 dump.append(" |");
804 
805                 // ASCII dump
806                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
807                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
808                 }
809                 dump.append('|');
810             }
811 
812             // Dump the last row which has less than 16 bytes.
813             if (remainder != 0) {
814                 int rowStartIndex = (fullRows << 4) + startIndex;
815                 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
816 
817                 // Hex dump
818                 int rowEndIndex = rowStartIndex + remainder;
819                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
820                     dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
821                 }
822                 dump.append(HEXPADDING[remainder]);
823                 dump.append(" |");
824 
825                 // Ascii dump
826                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
827                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
828                 }
829                 dump.append(BYTEPADDING[remainder]);
830                 dump.append('|');
831             }
832 
833             dump.append(NEWLINE +
834                         "+--------+-------------------------------------------------+----------------+");
835         }
836 
837         private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
838             if (row < HEXDUMP_ROWPREFIXES.length) {
839                 dump.append(HEXDUMP_ROWPREFIXES[row]);
840             } else {
841                 dump.append(NEWLINE);
842                 dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
843                 dump.setCharAt(dump.length() - 9, '|');
844                 dump.append('|');
845             }
846         }
847     }
848 
849     /**
850      * Returns a cached thread-local direct buffer, if available.
851      *
852      * @return a cached thread-local direct buffer, if available.  {@code null} otherwise.
853      */
854     public static ByteBuf threadLocalDirectBuffer() {
855         if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
856             return null;
857         }
858 
859         if (PlatformDependent.hasUnsafe()) {
860             return ThreadLocalUnsafeDirectByteBuf.newInstance();
861         } else {
862             return ThreadLocalDirectByteBuf.newInstance();
863         }
864     }
865 
866     static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
867 
868         private static final Recycler<ThreadLocalUnsafeDirectByteBuf> RECYCLER =
869                 new Recycler<ThreadLocalUnsafeDirectByteBuf>() {
870                     @Override
871                     protected ThreadLocalUnsafeDirectByteBuf newObject(Handle handle) {
872                         return new ThreadLocalUnsafeDirectByteBuf(handle);
873                     }
874                 };
875 
876         static ThreadLocalUnsafeDirectByteBuf newInstance() {
877             ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get();
878             buf.setRefCnt(1);
879             return buf;
880         }
881 
882         private final Handle handle;
883 
884         private ThreadLocalUnsafeDirectByteBuf(Handle handle) {
885             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
886             this.handle = handle;
887         }
888 
889         @Override
890         protected void deallocate() {
891             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
892                 super.deallocate();
893             } else {
894                 clear();
895                 RECYCLER.recycle(this, handle);
896             }
897         }
898     }
899 
900     static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf {
901 
902         private static final Recycler<ThreadLocalDirectByteBuf> RECYCLER = new Recycler<ThreadLocalDirectByteBuf>() {
903             @Override
904             protected ThreadLocalDirectByteBuf newObject(Handle handle) {
905                 return new ThreadLocalDirectByteBuf(handle);
906             }
907         };
908 
909         static ThreadLocalDirectByteBuf newInstance() {
910             ThreadLocalDirectByteBuf buf = RECYCLER.get();
911             buf.setRefCnt(1);
912             return buf;
913         }
914 
915         private final Handle handle;
916 
917         private ThreadLocalDirectByteBuf(Handle handle) {
918             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
919             this.handle = handle;
920         }
921 
922         @Override
923         protected void deallocate() {
924             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
925                 super.deallocate();
926             } else {
927                 clear();
928                 RECYCLER.recycle(this, handle);
929             }
930         }
931     }
932 
933     private static class IndexOfProcessor implements ByteBufProcessor {
934         private final byte byteToFind;
935 
936         public IndexOfProcessor(byte byteToFind) {
937             this.byteToFind = byteToFind;
938         }
939 
940         @Override
941         public boolean process(byte value) {
942             return value != byteToFind;
943         }
944     }
945 
946     /**
947      * Returns {@code true} if the given {@link ByteBuf} is valid text using the given {@link Charset},
948      * otherwise return {@code false}.
949      *
950      * @param buf The given {@link ByteBuf}.
951      * @param charset The specified {@link Charset}.
952      */
953     public static boolean isText(ByteBuf buf, Charset charset) {
954         return isText(buf, buf.readerIndex(), buf.readableBytes(), charset);
955     }
956 
957     /**
958      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
959      * text using the given {@link Charset}, otherwise return {@code false}.
960      *
961      * @param buf The given {@link ByteBuf}.
962      * @param index The start index of the specified buffer.
963      * @param length The length of the specified buffer.
964      * @param charset The specified {@link Charset}.
965      *
966      * @throws IndexOutOfBoundsException if {@code index} + {@code length} is greater than {@code buf.readableBytes}
967      */
968     public static boolean isText(ByteBuf buf, int index, int length, Charset charset) {
969         checkNotNull(buf, "buf");
970         checkNotNull(charset, "charset");
971         final int maxIndex = buf.readerIndex() + buf.readableBytes();
972         if (index < 0 || length < 0 || index > maxIndex - length) {
973             throw new IndexOutOfBoundsException("index: " + index + " length: " + length);
974         }
975         if (charset.equals(CharsetUtil.UTF_8)) {
976             return isUtf8(buf, index, length);
977         } else if (charset.equals(CharsetUtil.US_ASCII)) {
978             return isAscii(buf, index, length);
979         } else {
980             CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT);
981             try {
982                 if (buf.nioBufferCount() == 1) {
983                     decoder.decode(buf.nioBuffer(index, length));
984                 } else {
985                     ByteBuf heapBuffer = buf.alloc().heapBuffer(length);
986                     try {
987                         heapBuffer.writeBytes(buf, index, length);
988                         decoder.decode(heapBuffer.internalNioBuffer(heapBuffer.readerIndex(), length));
989                     } finally {
990                         heapBuffer.release();
991                     }
992                 }
993                 return true;
994             } catch (CharacterCodingException ignore) {
995                 return false;
996             }
997         }
998     }
999 
1000     /**
1001      * Aborts on a byte which is not a valid ASCII character.
1002      */
1003     private static final ByteBufProcessor FIND_NON_ASCII = new ByteBufProcessor() {
1004         @Override
1005         public boolean process(byte value) {
1006             return value >= 0;
1007         }
1008     };
1009 
1010     /**
1011      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1012      * ASCII text, otherwise return {@code false}.
1013      *
1014      * @param buf    The given {@link ByteBuf}.
1015      * @param index  The start index of the specified buffer.
1016      * @param length The length of the specified buffer.
1017      */
1018     private static boolean isAscii(ByteBuf buf, int index, int length) {
1019         return buf.forEachByte(index, length, FIND_NON_ASCII) == -1;
1020     }
1021 
1022     /**
1023      * Returns {@code true} if the specified {@link ByteBuf} starting at {@code index} with {@code length} is valid
1024      * UTF8 text, otherwise return {@code false}.
1025      *
1026      * @param buf The given {@link ByteBuf}.
1027      * @param index The start index of the specified buffer.
1028      * @param length The length of the specified buffer.
1029      *
1030      * @see
1031      * <a href=http://www.ietf.org/rfc/rfc3629.txt>UTF-8 Definition</a>
1032      *
1033      * <pre>
1034      * 1. Bytes format of UTF-8
1035      *
1036      * The table below summarizes the format of these different octet types.
1037      * The letter x indicates bits available for encoding bits of the character number.
1038      *
1039      * Char. number range  |        UTF-8 octet sequence
1040      *    (hexadecimal)    |              (binary)
1041      * --------------------+---------------------------------------------
1042      * 0000 0000-0000 007F | 0xxxxxxx
1043      * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1044      * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1045      * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1046      * </pre>
1047      *
1048      * <pre>
1049      * 2. Syntax of UTF-8 Byte Sequences
1050      *
1051      * UTF8-octets = *( UTF8-char )
1052      * UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
1053      * UTF8-1      = %x00-7F
1054      * UTF8-2      = %xC2-DF UTF8-tail
1055      * UTF8-3      = %xE0 %xA0-BF UTF8-tail /
1056      *               %xE1-EC 2( UTF8-tail ) /
1057      *               %xED %x80-9F UTF8-tail /
1058      *               %xEE-EF 2( UTF8-tail )
1059      * UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) /
1060      *               %xF1-F3 3( UTF8-tail ) /
1061      *               %xF4 %x80-8F 2( UTF8-tail )
1062      * UTF8-tail   = %x80-BF
1063      * </pre>
1064      */
1065     private static boolean isUtf8(ByteBuf buf, int index, int length) {
1066         final int endIndex = index + length;
1067         while (index < endIndex) {
1068             byte b1 = buf.getByte(index++);
1069             byte b2, b3, b4;
1070             if ((b1 & 0x80) == 0) {
1071                 // 1 byte
1072                 continue;
1073             }
1074             if ((b1 & 0xE0) == 0xC0) {
1075                 // 2 bytes
1076                 //
1077                 // Bit/Byte pattern
1078                 // 110xxxxx    10xxxxxx
1079                 // C2..DF      80..BF
1080                 if (index >= endIndex) { // no enough bytes
1081                     return false;
1082                 }
1083                 b2 = buf.getByte(index++);
1084                 if ((b2 & 0xC0) != 0x80) { // 2nd byte not starts with 10
1085                     return false;
1086                 }
1087                 if ((b1 & 0xFF) < 0xC2) { // out of lower bound
1088                     return false;
1089                 }
1090             } else if ((b1 & 0xF0) == 0xE0) {
1091                 // 3 bytes
1092                 //
1093                 // Bit/Byte pattern
1094                 // 1110xxxx    10xxxxxx    10xxxxxx
1095                 // E0          A0..BF      80..BF
1096                 // E1..EC      80..BF      80..BF
1097                 // ED          80..9F      80..BF
1098                 // E1..EF      80..BF      80..BF
1099                 if (index > endIndex - 2) { // no enough bytes
1100                     return false;
1101                 }
1102                 b2 = buf.getByte(index++);
1103                 b3 = buf.getByte(index++);
1104                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { // 2nd or 3rd bytes not start with 10
1105                     return false;
1106                 }
1107                 if ((b1 & 0x0F) == 0x00 && (b2 & 0xFF) < 0xA0) { // out of lower bound
1108                     return false;
1109                 }
1110                 if ((b1 & 0x0F) == 0x0D && (b2 & 0xFF) > 0x9F) { // out of upper bound
1111                     return false;
1112                 }
1113             } else if ((b1 & 0xF8) == 0xF0) {
1114                 // 4 bytes
1115                 //
1116                 // Bit/Byte pattern
1117                 // 11110xxx    10xxxxxx    10xxxxxx    10xxxxxx
1118                 // F0          90..BF      80..BF      80..BF
1119                 // F1..F3      80..BF      80..BF      80..BF
1120                 // F4          80..8F      80..BF      80..BF
1121                 if (index > endIndex - 3) { // no enough bytes
1122                     return false;
1123                 }
1124                 b2 = buf.getByte(index++);
1125                 b3 = buf.getByte(index++);
1126                 b4 = buf.getByte(index++);
1127                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) {
1128                     // 2nd, 3rd or 4th bytes not start with 10
1129                     return false;
1130                 }
1131                 if ((b1 & 0xFF) > 0xF4 // b1 invalid
1132                         || (b1 & 0xFF) == 0xF0 && (b2 & 0xFF) < 0x90    // b2 out of lower bound
1133                         || (b1 & 0xFF) == 0xF4 && (b2 & 0xFF) > 0x8F) { // b2 out of upper bound
1134                     return false;
1135                 }
1136             } else {
1137                 return false;
1138             }
1139         }
1140         return true;
1141     }
1142 
1143     private ByteBufUtil() { }
1144 }