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 }