1
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.IllegalReferenceCountException;
22 import io.netty.util.concurrent.FastThreadLocal;
23 import io.netty.util.internal.MathUtil;
24 import io.netty.util.internal.ObjectPool;
25 import io.netty.util.internal.ObjectPool.Handle;
26 import io.netty.util.internal.ObjectPool.ObjectCreator;
27 import io.netty.util.internal.PlatformDependent;
28 import io.netty.util.internal.StringUtil;
29 import io.netty.util.internal.SystemPropertyUtil;
30 import io.netty.util.internal.logging.InternalLogger;
31 import io.netty.util.internal.logging.InternalLoggerFactory;
32
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.nio.CharBuffer;
38 import java.nio.charset.CharacterCodingException;
39 import java.nio.charset.Charset;
40 import java.nio.charset.CharsetDecoder;
41 import java.nio.charset.CharsetEncoder;
42 import java.nio.charset.CoderResult;
43 import java.nio.charset.CodingErrorAction;
44 import java.util.Arrays;
45 import java.util.Locale;
46
47 import static io.netty.util.internal.MathUtil.isOutOfBounds;
48 import static io.netty.util.internal.ObjectUtil.checkNotNull;
49 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
50 import static io.netty.util.internal.StringUtil.NEWLINE;
51 import static io.netty.util.internal.StringUtil.isSurrogate;
52
53
57 public final class ByteBufUtil {
58
59 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
60 private static final FastThreadLocal<byte[]> BYTE_ARRAYS = new FastThreadLocal<byte[]>() {
61 @Override
62 protected byte[] initialValue() throws Exception {
63 return PlatformDependent.allocateUninitializedArray(MAX_TL_ARRAY_LEN);
64 }
65 };
66
67 private static final byte WRITE_UTF_UNKNOWN = (byte) '?';
68 private static final int MAX_CHAR_BUFFER_SIZE;
69 private static final int THREAD_LOCAL_BUFFER_SIZE;
70 private static final int MAX_BYTES_PER_CHAR_UTF8 =
71 (int) CharsetUtil.encoder(CharsetUtil.UTF_8).maxBytesPerChar();
72
73 static final int WRITE_CHUNK_SIZE = 8192;
74 static final ByteBufAllocator DEFAULT_ALLOCATOR;
75
76 static {
77 String allocType = SystemPropertyUtil.get(
78 "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
79 allocType = allocType.toLowerCase(Locale.US).trim();
80
81 ByteBufAllocator alloc;
82 if ("unpooled".equals(allocType)) {
83 alloc = UnpooledByteBufAllocator.DEFAULT;
84 logger.debug("-Dio.netty.allocator.type: {}", allocType);
85 } else if ("pooled".equals(allocType)) {
86 alloc = PooledByteBufAllocator.DEFAULT;
87 logger.debug("-Dio.netty.allocator.type: {}", allocType);
88 } else {
89 alloc = PooledByteBufAllocator.DEFAULT;
90 logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
91 }
92
93 DEFAULT_ALLOCATOR = alloc;
94
95 THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 0);
96 logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
97
98 MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024);
99 logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
100 }
101
102 static final int MAX_TL_ARRAY_LEN = 1024;
103
104
107 static byte[] threadLocalTempArray(int minLength) {
108 return minLength <= MAX_TL_ARRAY_LEN ? BYTE_ARRAYS.get()
109 : PlatformDependent.allocateUninitializedArray(minLength);
110 }
111
112
115 public static boolean isAccessible(ByteBuf buffer) {
116 return buffer.isAccessible();
117 }
118
119
123 public static ByteBuf ensureAccessible(ByteBuf buffer) {
124 if (!buffer.isAccessible()) {
125 throw new IllegalReferenceCountException(buffer.refCnt());
126 }
127 return buffer;
128 }
129
130
134 public static String hexDump(ByteBuf buffer) {
135 return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
136 }
137
138
142 public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
143 return HexUtil.hexDump(buffer, fromIndex, length);
144 }
145
146
150 public static String hexDump(byte[] array) {
151 return hexDump(array, 0, array.length);
152 }
153
154
158 public static String hexDump(byte[] array, int fromIndex, int length) {
159 return HexUtil.hexDump(array, fromIndex, length);
160 }
161
162
165 public static byte decodeHexByte(CharSequence s, int pos) {
166 return StringUtil.decodeHexByte(s, pos);
167 }
168
169
172 public static byte[] decodeHexDump(CharSequence hexDump) {
173 return StringUtil.decodeHexDump(hexDump, 0, hexDump.length());
174 }
175
176
179 public static byte[] decodeHexDump(CharSequence hexDump, int fromIndex, int length) {
180 return StringUtil.decodeHexDump(hexDump, fromIndex, length);
181 }
182
183
190 public static boolean ensureWritableSuccess(int ensureWritableResult) {
191 return ensureWritableResult == 0 || ensureWritableResult == 2;
192 }
193
194
198 public static int hashCode(ByteBuf buffer) {
199 final int aLen = buffer.readableBytes();
200 final int intCount = aLen >>> 2;
201 final int byteCount = aLen & 3;
202
203 int hashCode = EmptyByteBuf.EMPTY_BYTE_BUF_HASH_CODE;
204 int arrayIndex = buffer.readerIndex();
205 if (buffer.order() == ByteOrder.BIG_ENDIAN) {
206 for (int i = intCount; i > 0; i --) {
207 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
208 arrayIndex += 4;
209 }
210 } else {
211 for (int i = intCount; i > 0; i --) {
212 hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
213 arrayIndex += 4;
214 }
215 }
216
217 for (int i = byteCount; i > 0; i --) {
218 hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
219 }
220
221 if (hashCode == 0) {
222 hashCode = 1;
223 }
224
225 return hashCode;
226 }
227
228
231 public static int indexOf(ByteBuf needle, ByteBuf haystack) {
232
233 int attempts = haystack.readableBytes() - needle.readableBytes() + 1;
234 for (int i = 0; i < attempts; i++) {
235 if (equals(needle, needle.readerIndex(),
236 haystack, haystack.readerIndex() + i,
237 needle.readableBytes())) {
238 return haystack.readerIndex() + i;
239 }
240 }
241 return -1;
242 }
243
244
252 public static boolean equals(ByteBuf a, int aStartIndex, ByteBuf b, int bStartIndex, int length) {
253 if (aStartIndex < 0 || bStartIndex < 0 || length < 0) {
254 throw new IllegalArgumentException("All indexes and lengths must be non-negative");
255 }
256 if (a.writerIndex() - length < aStartIndex || b.writerIndex() - length < bStartIndex) {
257 return false;
258 }
259
260 final int longCount = length >>> 3;
261 final int byteCount = length & 7;
262
263 if (a.order() == b.order()) {
264 for (int i = longCount; i > 0; i --) {
265 if (a.getLong(aStartIndex) != b.getLong(bStartIndex)) {
266 return false;
267 }
268 aStartIndex += 8;
269 bStartIndex += 8;
270 }
271 } else {
272 for (int i = longCount; i > 0; i --) {
273 if (a.getLong(aStartIndex) != swapLong(b.getLong(bStartIndex))) {
274 return false;
275 }
276 aStartIndex += 8;
277 bStartIndex += 8;
278 }
279 }
280
281 for (int i = byteCount; i > 0; i --) {
282 if (a.getByte(aStartIndex) != b.getByte(bStartIndex)) {
283 return false;
284 }
285 aStartIndex ++;
286 bStartIndex ++;
287 }
288
289 return true;
290 }
291
292
297 public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
298 final int aLen = bufferA.readableBytes();
299 if (aLen != bufferB.readableBytes()) {
300 return false;
301 }
302 return equals(bufferA, bufferA.readerIndex(), bufferB, bufferB.readerIndex(), aLen);
303 }
304
305
309 public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
310 final int aLen = bufferA.readableBytes();
311 final int bLen = bufferB.readableBytes();
312 final int minLength = Math.min(aLen, bLen);
313 final int uintCount = minLength >>> 2;
314 final int byteCount = minLength & 3;
315 int aIndex = bufferA.readerIndex();
316 int bIndex = bufferB.readerIndex();
317
318 if (uintCount > 0) {
319 boolean bufferAIsBigEndian = bufferA.order() == ByteOrder.BIG_ENDIAN;
320 final long res;
321 int uintCountIncrement = uintCount << 2;
322
323 if (bufferA.order() == bufferB.order()) {
324 res = bufferAIsBigEndian ? compareUintBigEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
325 compareUintLittleEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
326 } else {
327 res = bufferAIsBigEndian ? compareUintBigEndianA(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
328 compareUintBigEndianB(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
329 }
330 if (res != 0) {
331
332 return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, res));
333 }
334 aIndex += uintCountIncrement;
335 bIndex += uintCountIncrement;
336 }
337
338 for (int aEnd = aIndex + byteCount; aIndex < aEnd; ++aIndex, ++bIndex) {
339 int comp = bufferA.getUnsignedByte(aIndex) - bufferB.getUnsignedByte(bIndex);
340 if (comp != 0) {
341 return comp;
342 }
343 }
344
345 return aLen - bLen;
346 }
347
348 private static long compareUintBigEndian(
349 ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
350 for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
351 long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedInt(bIndex);
352 if (comp != 0) {
353 return comp;
354 }
355 }
356 return 0;
357 }
358
359 private static long compareUintLittleEndian(
360 ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
361 for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
362 long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedIntLE(bIndex);
363 if (comp != 0) {
364 return comp;
365 }
366 }
367 return 0;
368 }
369
370 private static long compareUintBigEndianA(
371 ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
372 for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
373 long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedIntLE(bIndex);
374 if (comp != 0) {
375 return comp;
376 }
377 }
378 return 0;
379 }
380
381 private static long compareUintBigEndianB(
382 ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
383 for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
384 long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedInt(bIndex);
385 if (comp != 0) {
386 return comp;
387 }
388 }
389 return 0;
390 }
391
392
396 public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
397 if (fromIndex <= toIndex) {
398 return firstIndexOf(buffer, fromIndex, toIndex, value);
399 } else {
400 return lastIndexOf(buffer, fromIndex, toIndex, value);
401 }
402 }
403
404
407 public static short swapShort(short value) {
408 return Short.reverseBytes(value);
409 }
410
411
414 public static int swapMedium(int value) {
415 int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
416 if ((swapped & 0x800000) != 0) {
417 swapped |= 0xff000000;
418 }
419 return swapped;
420 }
421
422
425 public static int swapInt(int value) {
426 return Integer.reverseBytes(value);
427 }
428
429
432 public static long swapLong(long value) {
433 return Long.reverseBytes(value);
434 }
435
436
439 @SuppressWarnings("deprecation")
440 public static ByteBuf writeShortBE(ByteBuf buf, int shortValue) {
441 return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeShort(shortValue) : buf.writeShortLE(shortValue);
442 }
443
444
447 @SuppressWarnings("deprecation")
448 public static ByteBuf setShortBE(ByteBuf buf, int index, int shortValue) {
449 return buf.order() == ByteOrder.BIG_ENDIAN? buf.setShort(index, shortValue) : buf.setShortLE(index, shortValue);
450 }
451
452
455 @SuppressWarnings("deprecation")
456 public static ByteBuf writeMediumBE(ByteBuf buf, int mediumValue) {
457 return buf.order() == ByteOrder.BIG_ENDIAN? buf.writeMedium(mediumValue) : buf.writeMediumLE(mediumValue);
458 }
459
460
463 public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) {
464 boolean release = true;
465 ByteBuf dst = alloc.buffer(length);
466 try {
467 buffer.readBytes(dst);
468 release = false;
469 return dst;
470 } finally {
471 if (release) {
472 dst.release();
473 }
474 }
475 }
476
477 private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
478 fromIndex = Math.max(fromIndex, 0);
479 if (fromIndex >= toIndex || buffer.capacity() == 0) {
480 return -1;
481 }
482
483 return buffer.forEachByte(fromIndex, toIndex - fromIndex, new ByteProcessor.IndexOfProcessor(value));
484 }
485
486 private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
487 int capacity = buffer.capacity();
488 fromIndex = Math.min(fromIndex, capacity);
489 if (fromIndex < 0 || capacity == 0) {
490 return -1;
491 }
492
493 return buffer.forEachByteDesc(toIndex, fromIndex - toIndex, new ByteProcessor.IndexOfProcessor(value));
494 }
495
496 private static CharSequence checkCharSequenceBounds(CharSequence seq, int start, int end) {
497 if (MathUtil.isOutOfBounds(start, end - start, seq.length())) {
498 throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end
499 + ") <= seq.length(" + seq.length() + ')');
500 }
501 return seq;
502 }
503
504
512 public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
513
514 ByteBuf buf = alloc.buffer(utf8MaxBytes(seq));
515 writeUtf8(buf, seq);
516 return buf;
517 }
518
519
527 public static int writeUtf8(ByteBuf buf, CharSequence seq) {
528 int seqLength = seq.length();
529 return reserveAndWriteUtf8Seq(buf, seq, 0, seqLength, utf8MaxBytes(seqLength));
530 }
531
532
536 public static int writeUtf8(ByteBuf buf, CharSequence seq, int start, int end) {
537 checkCharSequenceBounds(seq, start, end);
538 return reserveAndWriteUtf8Seq(buf, seq, start, end, utf8MaxBytes(end - start));
539 }
540
541
550 public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int reserveBytes) {
551 return reserveAndWriteUtf8Seq(buf, seq, 0, seq.length(), reserveBytes);
552 }
553
554
561 public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int start, int end, int reserveBytes) {
562 return reserveAndWriteUtf8Seq(buf, checkCharSequenceBounds(seq, start, end), start, end, reserveBytes);
563 }
564
565 private static int reserveAndWriteUtf8Seq(ByteBuf buf, CharSequence seq, int start, int end, int reserveBytes) {
566 for (;;) {
567 if (buf instanceof WrappedCompositeByteBuf) {
568
569 buf = buf.unwrap();
570 } else if (buf instanceof AbstractByteBuf) {
571 AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
572 byteBuf.ensureWritable0(reserveBytes);
573 int written = writeUtf8(byteBuf, byteBuf.writerIndex, reserveBytes, seq, start, end);
574 byteBuf.writerIndex += written;
575 return written;
576 } else if (buf instanceof WrappedByteBuf) {
577
578 buf = buf.unwrap();
579 } else {
580 byte[] bytes = seq.subSequence(start, end).toString().getBytes(CharsetUtil.UTF_8);
581 buf.writeBytes(bytes);
582 return bytes.length;
583 }
584 }
585 }
586
587 static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes, CharSequence seq, int len) {
588 return writeUtf8(buffer, writerIndex, reservedBytes, seq, 0, len);
589 }
590
591
592 static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes,
593 CharSequence seq, int start, int end) {
594 if (seq instanceof AsciiString) {
595 writeAsciiString(buffer, writerIndex, (AsciiString) seq, start, end);
596 return end - start;
597 }
598 if (PlatformDependent.hasUnsafe()) {
599 if (buffer.hasArray()) {
600 return unsafeWriteUtf8(buffer.array(), PlatformDependent.byteArrayBaseOffset(),
601 buffer.arrayOffset() + writerIndex, seq, start, end);
602 }
603 if (buffer.hasMemoryAddress()) {
604 return unsafeWriteUtf8(null, buffer.memoryAddress(), writerIndex, seq, start, end);
605 }
606 } else {
607 if (buffer.hasArray()) {
608 return safeArrayWriteUtf8(buffer.array(), buffer.arrayOffset() + writerIndex, seq, start, end);
609 }
610 if (buffer.isDirect()) {
611 assert buffer.nioBufferCount() == 1;
612 final ByteBuffer internalDirectBuffer = buffer.internalNioBuffer(writerIndex, reservedBytes);
613 final int bufferPosition = internalDirectBuffer.position();
614 return safeDirectWriteUtf8(internalDirectBuffer, bufferPosition, seq, start, end);
615 }
616 }
617 return safeWriteUtf8(buffer, writerIndex, seq, start, end);
618 }
619
620
621 static void writeAsciiString(AbstractByteBuf buffer, int writerIndex, AsciiString seq, int start, int end) {
622 final int begin = seq.arrayOffset() + start;
623 final int length = end - start;
624 if (PlatformDependent.hasUnsafe()) {
625 if (buffer.hasArray()) {
626 PlatformDependent.copyMemory(seq.array(), begin,
627 buffer.array(), buffer.arrayOffset() + writerIndex, length);
628 return;
629 }
630 if (buffer.hasMemoryAddress()) {
631 PlatformDependent.copyMemory(seq.array(), begin, buffer.memoryAddress() + writerIndex, length);
632 return;
633 }
634 }
635 if (buffer.hasArray()) {
636 System.arraycopy(seq.array(), begin, buffer.array(), buffer.arrayOffset() + writerIndex, length);
637 return;
638 }
639 buffer.setBytes(writerIndex, seq.array(), begin, length);
640 }
641
642
643 private static int safeDirectWriteUtf8(ByteBuffer buffer, int writerIndex, CharSequence seq, int start, int end) {
644 assert !(seq instanceof AsciiString);
645 int oldWriterIndex = writerIndex;
646
647
648
649 for (int i = start; i < end; i++) {
650 char c = seq.charAt(i);
651 if (c < 0x80) {
652 buffer.put(writerIndex++, (byte) c);
653 } else if (c < 0x800) {
654 buffer.put(writerIndex++, (byte) (0xc0 | (c >> 6)));
655 buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
656 } else if (isSurrogate(c)) {
657 if (!Character.isHighSurrogate(c)) {
658 buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
659 continue;
660 }
661
662 if (++i == end) {
663 buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
664 break;
665 }
666
667
668 char c2 = seq.charAt(i);
669 if (!Character.isLowSurrogate(c2)) {
670 buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
671 buffer.put(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : (byte) c2);
672 } else {
673 int codePoint = Character.toCodePoint(c, c2);
674
675 buffer.put(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
676 buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
677 buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
678 buffer.put(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
679 }
680 } else {
681 buffer.put(writerIndex++, (byte) (0xe0 | (c >> 12)));
682 buffer.put(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
683 buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
684 }
685 }
686 return writerIndex - oldWriterIndex;
687 }
688
689
690 private static int safeWriteUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) {
691 assert !(seq instanceof AsciiString);
692 int oldWriterIndex = writerIndex;
693
694
695
696 for (int i = start; i < end; i++) {
697 char c = seq.charAt(i);
698 if (c < 0x80) {
699 buffer._setByte(writerIndex++, (byte) c);
700 } else if (c < 0x800) {
701 buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6)));
702 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
703 } else if (isSurrogate(c)) {
704 if (!Character.isHighSurrogate(c)) {
705 buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
706 continue;
707 }
708
709 if (++i == end) {
710 buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
711 break;
712 }
713
714
715 char c2 = seq.charAt(i);
716 if (!Character.isLowSurrogate(c2)) {
717 buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
718 buffer._setByte(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
719 } else {
720 int codePoint = Character.toCodePoint(c, c2);
721
722 buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
723 buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
724 buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
725 buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
726 }
727 } else {
728 buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
729 buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
730 buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
731 }
732 }
733 return writerIndex - oldWriterIndex;
734 }
735
736
737 private static int safeArrayWriteUtf8(byte[] buffer, int writerIndex, CharSequence seq, int start, int end) {
738 int oldWriterIndex = writerIndex;
739 for (int i = start; i < end; i++) {
740 char c = seq.charAt(i);
741 if (c < 0x80) {
742 buffer[writerIndex++] = (byte) c;
743 } else if (c < 0x800) {
744 buffer[writerIndex++] = (byte) (0xc0 | (c >> 6));
745 buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
746 } else if (isSurrogate(c)) {
747 if (!Character.isHighSurrogate(c)) {
748 buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
749 continue;
750 }
751
752 if (++i == end) {
753 buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
754 break;
755 }
756 char c2 = seq.charAt(i);
757
758
759 if (!Character.isLowSurrogate(c2)) {
760 buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
761 buffer[writerIndex++] = (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
762 } else {
763 int codePoint = Character.toCodePoint(c, c2);
764
765 buffer[writerIndex++] = (byte) (0xf0 | (codePoint >> 18));
766 buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 12) & 0x3f));
767 buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 6) & 0x3f));
768 buffer[writerIndex++] = (byte) (0x80 | (codePoint & 0x3f));
769 }
770 } else {
771 buffer[writerIndex++] = (byte) (0xe0 | (c >> 12));
772 buffer[writerIndex++] = (byte) (0x80 | ((c >> 6) & 0x3f));
773 buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
774 }
775 }
776 return writerIndex - oldWriterIndex;
777 }
778
779
780 private static int unsafeWriteUtf8(byte[] buffer, long memoryOffset, int writerIndex,
781 CharSequence seq, int start, int end) {
782 assert !(seq instanceof AsciiString);
783 long writerOffset = memoryOffset + writerIndex;
784 final long oldWriterOffset = writerOffset;
785 for (int i = start; i < end; i++) {
786 char c = seq.charAt(i);
787 if (c < 0x80) {
788 PlatformDependent.putByte(buffer, writerOffset++, (byte) c);
789 } else if (c < 0x800) {
790 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xc0 | (c >> 6)));
791 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
792 } else if (isSurrogate(c)) {
793 if (!Character.isHighSurrogate(c)) {
794 PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
795 continue;
796 }
797
798 if (++i == end) {
799 PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
800 break;
801 }
802 char c2 = seq.charAt(i);
803
804
805 if (!Character.isLowSurrogate(c2)) {
806 PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
807 PlatformDependent.putByte(buffer, writerOffset++,
808 (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2));
809 } else {
810 int codePoint = Character.toCodePoint(c, c2);
811
812 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xf0 | (codePoint >> 18)));
813 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
814 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
815 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (codePoint & 0x3f)));
816 }
817 } else {
818 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xe0 | (c >> 12)));
819 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((c >> 6) & 0x3f)));
820 PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
821 }
822 }
823 return (int) (writerOffset - oldWriterOffset);
824 }
825
826
829 public static int utf8MaxBytes(final int seqLength) {
830 return seqLength * MAX_BYTES_PER_CHAR_UTF8;
831 }
832
833
838 public static int utf8MaxBytes(CharSequence seq) {
839 return utf8MaxBytes(seq.length());
840 }
841
842
847 public static int utf8Bytes(final CharSequence seq) {
848 return utf8ByteCount(seq, 0, seq.length());
849 }
850
851
857 public static int utf8Bytes(final CharSequence seq, int start, int end) {
858 return utf8ByteCount(checkCharSequenceBounds(seq, start, end), start, end);
859 }
860
861 private static int utf8ByteCount(final CharSequence seq, int start, int end) {
862 if (seq instanceof AsciiString) {
863 return end - start;
864 }
865 int i = start;
866
867 while (i < end && seq.charAt(i) < 0x80) {
868 ++i;
869 }
870
871 return i < end ? (i - start) + utf8BytesNonAscii(seq, i, end) : i - start;
872 }
873
874 private static int utf8BytesNonAscii(final CharSequence seq, final int start, final int end) {
875 int encodedLength = 0;
876 for (int i = start; i < end; i++) {
877 final char c = seq.charAt(i);
878
879 if (c < 0x800) {
880
881 encodedLength += ((0x7f - c) >>> 31) + 1;
882 } else if (isSurrogate(c)) {
883 if (!Character.isHighSurrogate(c)) {
884 encodedLength++;
885
886 continue;
887 }
888
889 if (++i == end) {
890 encodedLength++;
891
892 break;
893 }
894 if (!Character.isLowSurrogate(seq.charAt(i))) {
895
896 encodedLength += 2;
897 continue;
898 }
899
900 encodedLength += 4;
901 } else {
902 encodedLength += 3;
903 }
904 }
905 return encodedLength;
906 }
907
908
916 public static ByteBuf writeAscii(ByteBufAllocator alloc, CharSequence seq) {
917
918 ByteBuf buf = alloc.buffer(seq.length());
919 writeAscii(buf, seq);
920 return buf;
921 }
922
923
929 public static int writeAscii(ByteBuf buf, CharSequence seq) {
930
931 for (;;) {
932 if (buf instanceof WrappedCompositeByteBuf) {
933
934 buf = buf.unwrap();
935 } else if (buf instanceof AbstractByteBuf) {
936 final int len = seq.length();
937 AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
938 byteBuf.ensureWritable0(len);
939 if (seq instanceof AsciiString) {
940 writeAsciiString(byteBuf, byteBuf.writerIndex, (AsciiString) seq, 0, len);
941 } else {
942 final int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
943 assert written == len;
944 }
945 byteBuf.writerIndex += len;
946 return len;
947 } else if (buf instanceof WrappedByteBuf) {
948
949 buf = buf.unwrap();
950 } else {
951 byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII);
952 buf.writeBytes(bytes);
953 return bytes.length;
954 }
955 }
956 }
957
958
959 static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
960
961
962
963 for (int i = 0; i < len; i++) {
964 buffer._setByte(writerIndex++, AsciiString.c2b(seq.charAt(i)));
965 }
966 return len;
967 }
968
969
973 public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
974 return encodeString0(alloc, false, src, charset, 0);
975 }
976
977
986 public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset, int extraCapacity) {
987 return encodeString0(alloc, false, src, charset, extraCapacity);
988 }
989
990 static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset,
991 int extraCapacity) {
992 final CharsetEncoder encoder = CharsetUtil.encoder(charset);
993 int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar()) + extraCapacity;
994 boolean release = true;
995 final ByteBuf dst;
996 if (enforceHeap) {
997 dst = alloc.heapBuffer(length);
998 } else {
999 dst = alloc.buffer(length);
1000 }
1001 try {
1002 final ByteBuffer dstBuf = dst.internalNioBuffer(dst.readerIndex(), length);
1003 final int pos = dstBuf.position();
1004 CoderResult cr = encoder.encode(src, dstBuf, true);
1005 if (!cr.isUnderflow()) {
1006 cr.throwException();
1007 }
1008 cr = encoder.flush(dstBuf);
1009 if (!cr.isUnderflow()) {
1010 cr.throwException();
1011 }
1012 dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
1013 release = false;
1014 return dst;
1015 } catch (CharacterCodingException x) {
1016 throw new IllegalStateException(x);
1017 } finally {
1018 if (release) {
1019 dst.release();
1020 }
1021 }
1022 }
1023
1024 @SuppressWarnings("deprecation")
1025 static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) {
1026 if (len == 0) {
1027 return StringUtil.EMPTY_STRING;
1028 }
1029 final byte[] array;
1030 final int offset;
1031
1032 if (src.hasArray()) {
1033 array = src.array();
1034 offset = src.arrayOffset() + readerIndex;
1035 } else {
1036 array = threadLocalTempArray(len);
1037 offset = 0;
1038 src.getBytes(readerIndex, array, 0, len);
1039 }
1040 if (CharsetUtil.US_ASCII.equals(charset)) {
1041
1042 return new String(array, 0, offset, len);
1043 }
1044 return new String(array, offset, len, charset);
1045 }
1046
1047
1052 public static ByteBuf threadLocalDirectBuffer() {
1053 if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
1054 return null;
1055 }
1056
1057 if (PlatformDependent.hasUnsafe()) {
1058 return ThreadLocalUnsafeDirectByteBuf.newInstance();
1059 } else {
1060 return ThreadLocalDirectByteBuf.newInstance();
1061 }
1062 }
1063
1064
1068 public static byte[] getBytes(ByteBuf buf) {
1069 return getBytes(buf, buf.readerIndex(), buf.readableBytes());
1070 }
1071
1072
1076 public static byte[] getBytes(ByteBuf buf, int start, int length) {
1077 return getBytes(buf, start, length, true);
1078 }
1079
1080
1086 public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
1087 int capacity = buf.capacity();
1088 if (isOutOfBounds(start, length, capacity)) {
1089 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
1090 + ") <= " + "buf.capacity(" + capacity + ')');
1091 }
1092
1093 if (buf.hasArray()) {
1094 int baseOffset = buf.arrayOffset() + start;
1095 byte[] bytes = buf.array();
1096 if (copy || baseOffset != 0 || length != bytes.length) {
1097 return Arrays.copyOfRange(bytes, baseOffset, baseOffset + length);
1098 } else {
1099 return bytes;
1100 }
1101 }
1102
1103 byte[] bytes = PlatformDependent.allocateUninitializedArray(length);
1104 buf.getBytes(start, bytes);
1105 return bytes;
1106 }
1107
1108
1114 public static void copy(AsciiString src, ByteBuf dst) {
1115 copy(src, 0, dst, src.length());
1116 }
1117
1118
1129 public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
1130 if (isOutOfBounds(srcIdx, length, src.length())) {
1131 throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
1132 + length + ") <= srcLen(" + src.length() + ')');
1133 }
1134
1135 checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
1136 }
1137
1138
1146 public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
1147 if (isOutOfBounds(srcIdx, length, src.length())) {
1148 throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
1149 + length + ") <= srcLen(" + src.length() + ')');
1150 }
1151
1152 checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
1153 }
1154
1155
1158 public static String prettyHexDump(ByteBuf buffer) {
1159 return prettyHexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
1160 }
1161
1162
1166 public static String prettyHexDump(ByteBuf buffer, int offset, int length) {
1167 return HexUtil.prettyHexDump(buffer, offset, length);
1168 }
1169
1170
1174 public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf) {
1175 appendPrettyHexDump(dump, buf, buf.readerIndex(), buf.readableBytes());
1176 }
1177
1178
1183 public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
1184 HexUtil.appendPrettyHexDump(dump, buf, offset, length);
1185 }
1186
1187
1188 private static final class HexUtil {
1189
1190 private static final char[] BYTE2CHAR = new char[256];
1191 private static final char[] HEXDUMP_TABLE = new char[256 * 4];
1192 private static final String[] HEXPADDING = new String[16];
1193 private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
1194 private static final String[] BYTE2HEX = new String[256];
1195 private static final String[] BYTEPADDING = new String[16];
1196
1197 static {
1198 final char[] DIGITS = "0123456789abcdef".toCharArray();
1199 for (int i = 0; i < 256; i ++) {
1200 HEXDUMP_TABLE[ i << 1 ] = DIGITS[i >>> 4 & 0x0F];
1201 HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];
1202 }
1203
1204 int i;
1205
1206
1207 for (i = 0; i < HEXPADDING.length; i ++) {
1208 int padding = HEXPADDING.length - i;
1209 StringBuilder buf = new StringBuilder(padding * 3);
1210 for (int j = 0; j < padding; j ++) {
1211 buf.append(" ");
1212 }
1213 HEXPADDING[i] = buf.toString();
1214 }
1215
1216
1217 for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) {
1218 StringBuilder buf = new StringBuilder(12);
1219 buf.append(NEWLINE);
1220 buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
1221 buf.setCharAt(buf.length() - 9, '|');
1222 buf.append('|');
1223 HEXDUMP_ROWPREFIXES[i] = buf.toString();
1224 }
1225
1226
1227 for (i = 0; i < BYTE2HEX.length; i ++) {
1228 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
1229 }
1230
1231
1232 for (i = 0; i < BYTEPADDING.length; i ++) {
1233 int padding = BYTEPADDING.length - i;
1234 StringBuilder buf = new StringBuilder(padding);
1235 for (int j = 0; j < padding; j ++) {
1236 buf.append(' ');
1237 }
1238 BYTEPADDING[i] = buf.toString();
1239 }
1240
1241
1242 for (i = 0; i < BYTE2CHAR.length; i ++) {
1243 if (i <= 0x1f || i >= 0x7f) {
1244 BYTE2CHAR[i] = '.';
1245 } else {
1246 BYTE2CHAR[i] = (char) i;
1247 }
1248 }
1249 }
1250
1251 private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
1252 checkPositiveOrZero(length, "length");
1253 if (length == 0) {
1254 return "";
1255 }
1256
1257 int endIndex = fromIndex + length;
1258 char[] buf = new char[length << 1];
1259
1260 int srcIdx = fromIndex;
1261 int dstIdx = 0;
1262 for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1263 System.arraycopy(
1264 HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
1265 buf, dstIdx, 2);
1266 }
1267
1268 return new String(buf);
1269 }
1270
1271 private static String hexDump(byte[] array, int fromIndex, int length) {
1272 checkPositiveOrZero(length, "length");
1273 if (length == 0) {
1274 return "";
1275 }
1276
1277 int endIndex = fromIndex + length;
1278 char[] buf = new char[length << 1];
1279
1280 int srcIdx = fromIndex;
1281 int dstIdx = 0;
1282 for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
1283 System.arraycopy(
1284 HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1,
1285 buf, dstIdx, 2);
1286 }
1287
1288 return new String(buf);
1289 }
1290
1291 private static String prettyHexDump(ByteBuf buffer, int offset, int length) {
1292 if (length == 0) {
1293 return StringUtil.EMPTY_STRING;
1294 } else {
1295 int rows = length / 16 + ((length & 15) == 0? 0 : 1) + 4;
1296 StringBuilder buf = new StringBuilder(rows * 80);
1297 appendPrettyHexDump(buf, buffer, offset, length);
1298 return buf.toString();
1299 }
1300 }
1301
1302 private static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
1303 if (isOutOfBounds(offset, length, buf.capacity())) {
1304 throw new IndexOutOfBoundsException(
1305 "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
1306 + ") <= " + "buf.capacity(" + buf.capacity() + ')');
1307 }
1308 if (length == 0) {
1309 return;
1310 }
1311 dump.append(
1312 " +-------------------------------------------------+" +
1313 NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" +
1314 NEWLINE + "+--------+-------------------------------------------------+----------------+");
1315
1316 final int startIndex = offset;
1317 final int fullRows = length >>> 4;
1318 final int remainder = length & 0xF;
1319
1320
1321 for (int row = 0; row < fullRows; row ++) {
1322 int rowStartIndex = (row << 4) + startIndex;
1323
1324
1325 appendHexDumpRowPrefix(dump, row, rowStartIndex);
1326
1327
1328 int rowEndIndex = rowStartIndex + 16;
1329 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1330 dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1331 }
1332 dump.append(" |");
1333
1334
1335 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1336 dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1337 }
1338 dump.append('|');
1339 }
1340
1341
1342 if (remainder != 0) {
1343 int rowStartIndex = (fullRows << 4) + startIndex;
1344 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
1345
1346
1347 int rowEndIndex = rowStartIndex + remainder;
1348 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1349 dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
1350 }
1351 dump.append(HEXPADDING[remainder]);
1352 dump.append(" |");
1353
1354
1355 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
1356 dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
1357 }
1358 dump.append(BYTEPADDING[remainder]);
1359 dump.append('|');
1360 }
1361
1362 dump.append(NEWLINE +
1363 "+--------+-------------------------------------------------+----------------+");
1364 }
1365
1366 private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
1367 if (row < HEXDUMP_ROWPREFIXES.length) {
1368 dump.append(HEXDUMP_ROWPREFIXES[row]);
1369 } else {
1370 dump.append(NEWLINE);
1371 dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
1372 dump.setCharAt(dump.length() - 9, '|');
1373 dump.append('|');
1374 }
1375 }
1376 }
1377
1378 static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
1379
1380 private static final ObjectPool<ThreadLocalUnsafeDirectByteBuf> RECYCLER =
1381 ObjectPool.newPool(new ObjectCreator<ThreadLocalUnsafeDirectByteBuf>() {
1382 @Override
1383 public ThreadLocalUnsafeDirectByteBuf newObject(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1384 return new ThreadLocalUnsafeDirectByteBuf(handle);
1385 }
1386 });
1387
1388 static ThreadLocalUnsafeDirectByteBuf newInstance() {
1389 ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get();
1390 buf.resetRefCnt();
1391 return buf;
1392 }
1393
1394 private final Handle<ThreadLocalUnsafeDirectByteBuf> handle;
1395
1396 private ThreadLocalUnsafeDirectByteBuf(Handle<ThreadLocalUnsafeDirectByteBuf> handle) {
1397 super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1398 this.handle = handle;
1399 }
1400
1401 @Override
1402 protected void deallocate() {
1403 if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1404 super.deallocate();
1405 } else {
1406 clear();
1407 handle.recycle(this);
1408 }
1409 }
1410 }
1411
1412 static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf {
1413
1414 private static final ObjectPool<ThreadLocalDirectByteBuf> RECYCLER = ObjectPool.newPool(
1415 new ObjectCreator<ThreadLocalDirectByteBuf>() {
1416 @Override
1417 public ThreadLocalDirectByteBuf newObject(Handle<ThreadLocalDirectByteBuf> handle) {
1418 return new ThreadLocalDirectByteBuf(handle);
1419 }
1420 });
1421
1422 static ThreadLocalDirectByteBuf newInstance() {
1423 ThreadLocalDirectByteBuf buf = RECYCLER.get();
1424 buf.resetRefCnt();
1425 return buf;
1426 }
1427
1428 private final Handle<ThreadLocalDirectByteBuf> handle;
1429
1430 private ThreadLocalDirectByteBuf(Handle<ThreadLocalDirectByteBuf> handle) {
1431 super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
1432 this.handle = handle;
1433 }
1434
1435 @Override
1436 protected void deallocate() {
1437 if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
1438 super.deallocate();
1439 } else {
1440 clear();
1441 handle.recycle(this);
1442 }
1443 }
1444 }
1445
1446
1453 public static boolean isText(ByteBuf buf, Charset charset) {
1454 return isText(buf, buf.readerIndex(), buf.readableBytes(), charset);
1455 }
1456
1457
1468 public static boolean isText(ByteBuf buf, int index, int length, Charset charset) {
1469 checkNotNull(buf, "buf");
1470 checkNotNull(charset, "charset");
1471 final int maxIndex = buf.readerIndex() + buf.readableBytes();
1472 if (index < 0 || length < 0 || index > maxIndex - length) {
1473 throw new IndexOutOfBoundsException("index: " + index + " length: " + length);
1474 }
1475 if (charset.equals(CharsetUtil.UTF_8)) {
1476 return isUtf8(buf, index, length);
1477 } else if (charset.equals(CharsetUtil.US_ASCII)) {
1478 return isAscii(buf, index, length);
1479 } else {
1480 CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT);
1481 try {
1482 if (buf.nioBufferCount() == 1) {
1483 decoder.decode(buf.nioBuffer(index, length));
1484 } else {
1485 ByteBuf heapBuffer = buf.alloc().heapBuffer(length);
1486 try {
1487 heapBuffer.writeBytes(buf, index, length);
1488 decoder.decode(heapBuffer.internalNioBuffer(heapBuffer.readerIndex(), length));
1489 } finally {
1490 heapBuffer.release();
1491 }
1492 }
1493 return true;
1494 } catch (CharacterCodingException ignore) {
1495 return false;
1496 }
1497 }
1498 }
1499
1500
1503 private static final ByteProcessor FIND_NON_ASCII = new ByteProcessor() {
1504 @Override
1505 public boolean process(byte value) {
1506 return value >= 0;
1507 }
1508 };
1509
1510
1518 private static boolean isAscii(ByteBuf buf, int index, int length) {
1519 return buf.forEachByte(index, length, FIND_NON_ASCII) == -1;
1520 }
1521
1522
1565 private static boolean isUtf8(ByteBuf buf, int index, int length) {
1566 final int endIndex = index + length;
1567 while (index < endIndex) {
1568 byte b1 = buf.getByte(index++);
1569 byte b2, b3, b4;
1570 if ((b1 & 0x80) == 0) {
1571
1572 continue;
1573 }
1574 if ((b1 & 0xE0) == 0xC0) {
1575
1576
1577
1578
1579
1580 if (index >= endIndex) {
1581 return false;
1582 }
1583 b2 = buf.getByte(index++);
1584 if ((b2 & 0xC0) != 0x80) {
1585 return false;
1586 }
1587 if ((b1 & 0xFF) < 0xC2) {
1588 return false;
1589 }
1590 } else if ((b1 & 0xF0) == 0xE0) {
1591
1592
1593
1594
1595
1596
1597
1598
1599 if (index > endIndex - 2) {
1600 return false;
1601 }
1602 b2 = buf.getByte(index++);
1603 b3 = buf.getByte(index++);
1604 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
1605 return false;
1606 }
1607 if ((b1 & 0x0F) == 0x00 && (b2 & 0xFF) < 0xA0) {
1608 return false;
1609 }
1610 if ((b1 & 0x0F) == 0x0D && (b2 & 0xFF) > 0x9F) {
1611 return false;
1612 }
1613 } else if ((b1 & 0xF8) == 0xF0) {
1614
1615
1616
1617
1618
1619
1620
1621 if (index > endIndex - 3) {
1622 return false;
1623 }
1624 b2 = buf.getByte(index++);
1625 b3 = buf.getByte(index++);
1626 b4 = buf.getByte(index++);
1627 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) {
1628
1629 return false;
1630 }
1631 if ((b1 & 0xFF) > 0xF4
1632 || (b1 & 0xFF) == 0xF0 && (b2 & 0xFF) < 0x90
1633 || (b1 & 0xFF) == 0xF4 && (b2 & 0xFF) > 0x8F) {
1634 return false;
1635 }
1636 } else {
1637 return false;
1638 }
1639 }
1640 return true;
1641 }
1642
1643
1647 static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int position, int length, OutputStream out)
1648 throws IOException {
1649 if (buffer.hasArray()) {
1650 out.write(buffer.array(), position + buffer.arrayOffset(), length);
1651 } else {
1652 int chunkLen = Math.min(length, WRITE_CHUNK_SIZE);
1653 buffer.clear().position(position);
1654
1655 if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) {
1656 getBytes(buffer, threadLocalTempArray(chunkLen), 0, chunkLen, out, length);
1657 } else {
1658
1659 ByteBuf tmpBuf = allocator.heapBuffer(chunkLen);
1660 try {
1661 byte[] tmp = tmpBuf.array();
1662 int offset = tmpBuf.arrayOffset();
1663 getBytes(buffer, tmp, offset, chunkLen, out, length);
1664 } finally {
1665 tmpBuf.release();
1666 }
1667 }
1668 }
1669 }
1670
1671 private static void getBytes(ByteBuffer inBuffer, byte[] in, int inOffset, int inLen, OutputStream out, int outLen)
1672 throws IOException {
1673 do {
1674 int len = Math.min(inLen, outLen);
1675 inBuffer.get(in, inOffset, len);
1676 out.write(in, inOffset, len);
1677 outLen -= len;
1678 } while (outLen > 0);
1679 }
1680
1681 private ByteBufUtil() { }
1682 }
1683