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 }