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