1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
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  
46  
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  
91  
92  
93      public static String hexDump(ByteBuf buffer) {
94          return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
95      }
96  
97      
98  
99  
100 
101     public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
102         return HexUtil.hexDump(buffer, fromIndex, length);
103     }
104 
105     
106 
107 
108 
109     public static String hexDump(byte[] array) {
110         return hexDump(array, 0, array.length);
111     }
112 
113     
114 
115 
116 
117     public static String hexDump(byte[] array, int fromIndex, int length) {
118         return HexUtil.hexDump(array, fromIndex, length);
119     }
120 
121     
122 
123 
124     public static byte decodeHexByte(CharSequence s, int pos) {
125         return StringUtil.decodeHexByte(s, pos);
126     }
127 
128     
129 
130 
131     public static byte[] decodeHexDump(CharSequence hexDump) {
132         return StringUtil.decodeHexDump(hexDump, 0, hexDump.length());
133     }
134 
135     
136 
137 
138     public static byte[] decodeHexDump(CharSequence hexDump, int fromIndex, int length) {
139         return StringUtil.decodeHexDump(hexDump, fromIndex, length);
140     }
141 
142     
143 
144 
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 
178 
179 
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 
224 
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                 
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 
312 
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 
324 
325     public static short swapShort(short value) {
326         return Short.reverseBytes(value);
327     }
328 
329     
330 
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 
342 
343     public static int swapInt(int value) {
344         return Integer.reverseBytes(value);
345     }
346 
347     
348 
349 
350     public static long swapLong(long value) {
351         return Long.reverseBytes(value);
352     }
353 
354     
355 
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 
391 
392 
393 
394 
395 
396 
397     public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
398         
399         ByteBuf buf = alloc.buffer(seq.length() * MAX_BYTES_PER_CHAR_UTF8);
400         writeUtf8(buf, seq);
401         return buf;
402     }
403 
404     
405 
406 
407 
408 
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                 
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     
429     private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len) {
430         int oldWriterIndex = buffer.writerIndex;
431         int writerIndex = oldWriterIndex;
432 
433         
434         
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                     
450                     
451                     
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                 
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         
475         buffer.writerIndex = writerIndex;
476         return writerIndex - oldWriterIndex;
477     }
478 
479     
480 
481 
482 
483 
484 
485 
486 
487     public static ByteBuf writeAscii(ByteBufAllocator alloc, CharSequence seq) {
488         
489         ByteBuf buf = alloc.buffer(seq.length());
490         writeAscii(buf, seq);
491         return buf;
492     }
493 
494     
495 
496 
497 
498 
499 
500     public static int writeAscii(ByteBuf buf, CharSequence seq) {
501         
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                 
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     
521     private static void writeAscii(AbstractByteBuf buffer, CharSequence seq, int len) {
522         int writerIndex = buffer.writerIndex;
523 
524         
525         
526         for (int i = 0; i < len; i++) {
527             buffer._setByte(writerIndex++, (byte) seq.charAt(i));
528         }
529         
530         buffer.writerIndex = writerIndex;
531     }
532 
533     
534 
535 
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             
593             
594             ByteBuf buffer = src.alloc().heapBuffer(len);
595             try {
596                 buffer.writeBytes(src, readerIndex, len);
597                 
598                 decodeString(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst);
599             } finally {
600                 
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 
624 
625     public static String prettyHexDump(ByteBuf buffer) {
626         return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
627     }
628 
629     
630 
631 
632 
633     public static String prettyHexDump(ByteBuf buffer, int offset, int length) {
634         return HexUtil.prettyHexDump(buffer, offset, length);
635     }
636 
637     
638 
639 
640 
641     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) {
642         appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes());
643     }
644 
645     
646 
647 
648 
649 
650     public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
651         HexUtil.appendPrettyHexDump(dump, buf, offset, length);
652     }
653 
654     
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             
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             
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             
694             for (i = 0; i < BYTE2HEX.length; i ++) {
695                 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
696             }
697 
698             
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             
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             
792             for (int row = 0; row < fullRows; row ++) {
793                 int rowStartIndex = (row << 4) + startIndex;
794 
795                 
796                 appendHexDumpRowPrefix(dump, row, rowStartIndex);
797 
798                 
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                 
806                 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
807                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
808                 }
809                 dump.append('|');
810             }
811 
812             
813             if (remainder != 0) {
814                 int rowStartIndex = (fullRows << 4) + startIndex;
815                 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
816 
817                 
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                 
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 
851 
852 
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 
948 
949 
950 
951 
952 
953     public static boolean isText(ByteBuf buf, Charset charset) {
954         return isText(buf, buf.readerIndex(), buf.readableBytes(), charset);
955     }
956 
957     
958 
959 
960 
961 
962 
963 
964 
965 
966 
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 
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 
1012 
1013 
1014 
1015 
1016 
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 
1024 
1025 
1026 
1027 
1028 
1029 
1030 
1031 
1032 
1033 
1034 
1035 
1036 
1037 
1038 
1039 
1040 
1041 
1042 
1043 
1044 
1045 
1046 
1047 
1048 
1049 
1050 
1051 
1052 
1053 
1054 
1055 
1056 
1057 
1058 
1059 
1060 
1061 
1062 
1063 
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                 
1072                 continue;
1073             }
1074             if ((b1 & 0xE0) == 0xC0) {
1075                 
1076                 
1077                 
1078                 
1079                 
1080                 if (index >= endIndex) { 
1081                     return false;
1082                 }
1083                 b2 = buf.getByte(index++);
1084                 if ((b2 & 0xC0) != 0x80) { 
1085                     return false;
1086                 }
1087                 if ((b1 & 0xFF) < 0xC2) { 
1088                     return false;
1089                 }
1090             } else if ((b1 & 0xF0) == 0xE0) {
1091                 
1092                 
1093                 
1094                 
1095                 
1096                 
1097                 
1098                 
1099                 if (index > endIndex - 2) { 
1100                     return false;
1101                 }
1102                 b2 = buf.getByte(index++);
1103                 b3 = buf.getByte(index++);
1104                 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { 
1105                     return false;
1106                 }
1107                 if ((b1 & 0x0F) == 0x00 && (b2 & 0xFF) < 0xA0) { 
1108                     return false;
1109                 }
1110                 if ((b1 & 0x0F) == 0x0D && (b2 & 0xFF) > 0x9F) { 
1111                     return false;
1112                 }
1113             } else if ((b1 & 0xF8) == 0xF0) {
1114                 
1115                 
1116                 
1117                 
1118                 
1119                 
1120                 
1121                 if (index > endIndex - 3) { 
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                     
1129                     return false;
1130                 }
1131                 if ((b1 & 0xFF) > 0xF4 
1132                         || (b1 & 0xFF) == 0xF0 && (b2 & 0xFF) < 0x90    
1133                         || (b1 & 0xFF) == 0xF4 && (b2 & 0xFF) > 0x8F) { 
1134                     return false;
1135                 }
1136             } else {
1137                 return false;
1138             }
1139         }
1140         return true;
1141     }
1142 
1143     private ByteBufUtil() { }
1144 }