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