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 }