View Javadoc
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  package io.netty.buffer;
17  
18  import io.netty.util.CharsetUtil;
19  import io.netty.util.Recycler;
20  import io.netty.util.Recycler.Handle;
21  import io.netty.util.internal.PlatformDependent;
22  import io.netty.util.internal.SystemPropertyUtil;
23  import io.netty.util.internal.logging.InternalLogger;
24  import io.netty.util.internal.logging.InternalLoggerFactory;
25  
26  import java.nio.ByteBuffer;
27  import java.nio.ByteOrder;
28  import java.nio.CharBuffer;
29  import java.nio.charset.CharacterCodingException;
30  import java.nio.charset.Charset;
31  import java.nio.charset.CharsetDecoder;
32  import java.nio.charset.CharsetEncoder;
33  import java.nio.charset.CoderResult;
34  import java.util.Locale;
35  
36  /**
37   * A collection of utility methods that is related with handling {@link ByteBuf}.
38   */
39  public final class ByteBufUtil {
40  
41      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
42  
43      private static final char[] HEXDUMP_TABLE = new char[256 * 4];
44  
45      static final ByteBufAllocator DEFAULT_ALLOCATOR;
46  
47      private static final int THREAD_LOCAL_BUFFER_SIZE;
48  
49      static {
50          final char[] DIGITS = "0123456789abcdef".toCharArray();
51          for (int i = 0; i < 256; i ++) {
52              HEXDUMP_TABLE[ i << 1     ] = DIGITS[i >>> 4 & 0x0F];
53              HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i       & 0x0F];
54          }
55  
56          String allocType = SystemPropertyUtil.get(
57                  "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
58          allocType = allocType.toLowerCase(Locale.US).trim();
59  
60          ByteBufAllocator alloc;
61          if ("unpooled".equals(allocType)) {
62              alloc = UnpooledByteBufAllocator.DEFAULT;
63              logger.debug("-Dio.netty.allocator.type: {}", allocType);
64          } else if ("pooled".equals(allocType)) {
65              alloc = PooledByteBufAllocator.DEFAULT;
66              logger.debug("-Dio.netty.allocator.type: {}", allocType);
67          } else {
68              alloc = PooledByteBufAllocator.DEFAULT;
69              logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
70          }
71  
72          DEFAULT_ALLOCATOR = alloc;
73  
74          THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 64 * 1024);
75          logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
76      }
77  
78      /**
79       * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
80       * of the specified buffer's readable bytes.
81       */
82      public static String hexDump(ByteBuf buffer) {
83          return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
84      }
85  
86      /**
87       * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
88       * of the specified buffer's sub-region.
89       */
90      public static String hexDump(ByteBuf buffer, int fromIndex, int length) {
91          if (length < 0) {
92              throw new IllegalArgumentException("length: " + length);
93          }
94          if (length == 0) {
95              return "";
96          }
97  
98          int endIndex = fromIndex + length;
99          char[] buf = new char[length << 1];
100 
101         int srcIdx = fromIndex;
102         int dstIdx = 0;
103         for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
104             System.arraycopy(
105                     HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
106                     buf, dstIdx, 2);
107         }
108 
109         return new String(buf);
110     }
111 
112     /**
113      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
114      * of the specified byte array.
115      */
116     public static String hexDump(byte[] array) {
117         return hexDump(array, 0, array.length);
118     }
119 
120     /**
121      * Returns a <a href="http://en.wikipedia.org/wiki/Hex_dump">hex dump</a>
122      * of the specified byte array's sub-region.
123      */
124     public static String hexDump(byte[] array, int fromIndex, int length) {
125         if (length < 0) {
126             throw new IllegalArgumentException("length: " + length);
127         }
128         if (length == 0) {
129             return "";
130         }
131 
132         int endIndex = fromIndex + length;
133         char[] buf = new char[length << 1];
134 
135         int srcIdx = fromIndex;
136         int dstIdx = 0;
137         for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
138             System.arraycopy(HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1, buf, dstIdx, 2);
139         }
140 
141         return new String(buf);
142     }
143 
144     /**
145      * Calculates the hash code of the specified buffer.  This method is
146      * useful when implementing a new buffer type.
147      */
148     public static int hashCode(ByteBuf buffer) {
149         final int aLen = buffer.readableBytes();
150         final int intCount = aLen >>> 2;
151         final int byteCount = aLen & 3;
152 
153         int hashCode = 1;
154         int arrayIndex = buffer.readerIndex();
155         if (buffer.order() == ByteOrder.BIG_ENDIAN) {
156             for (int i = intCount; i > 0; i --) {
157                 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
158                 arrayIndex += 4;
159             }
160         } else {
161             for (int i = intCount; i > 0; i --) {
162                 hashCode = 31 * hashCode + swapInt(buffer.getInt(arrayIndex));
163                 arrayIndex += 4;
164             }
165         }
166 
167         for (int i = byteCount; i > 0; i --) {
168             hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
169         }
170 
171         if (hashCode == 0) {
172             hashCode = 1;
173         }
174 
175         return hashCode;
176     }
177 
178     /**
179      * Returns {@code true} if and only if the two specified buffers are
180      * identical to each other as described in {@code ChannelBuffer#equals(Object)}.
181      * This method is useful when implementing a new buffer type.
182      */
183     public static boolean equals(ByteBuf bufferA, ByteBuf bufferB) {
184         final int aLen = bufferA.readableBytes();
185         if (aLen != bufferB.readableBytes()) {
186             return false;
187         }
188 
189         final int longCount = aLen >>> 3;
190         final int byteCount = aLen & 7;
191 
192         int aIndex = bufferA.readerIndex();
193         int bIndex = bufferB.readerIndex();
194 
195         if (bufferA.order() == bufferB.order()) {
196             for (int i = longCount; i > 0; i --) {
197                 if (bufferA.getLong(aIndex) != bufferB.getLong(bIndex)) {
198                     return false;
199                 }
200                 aIndex += 8;
201                 bIndex += 8;
202             }
203         } else {
204             for (int i = longCount; i > 0; i --) {
205                 if (bufferA.getLong(aIndex) != swapLong(bufferB.getLong(bIndex))) {
206                     return false;
207                 }
208                 aIndex += 8;
209                 bIndex += 8;
210             }
211         }
212 
213         for (int i = byteCount; i > 0; i --) {
214             if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) {
215                 return false;
216             }
217             aIndex ++;
218             bIndex ++;
219         }
220 
221         return true;
222     }
223 
224     /**
225      * Compares the two specified buffers as described in {@link ByteBuf#compareTo(ByteBuf)}.
226      * This method is useful when implementing a new buffer type.
227      */
228     public static int compare(ByteBuf bufferA, ByteBuf bufferB) {
229         final int aLen = bufferA.readableBytes();
230         final int bLen = bufferB.readableBytes();
231         final int minLength = Math.min(aLen, bLen);
232         final int uintCount = minLength >>> 2;
233         final int byteCount = minLength & 3;
234 
235         int aIndex = bufferA.readerIndex();
236         int bIndex = bufferB.readerIndex();
237 
238         if (bufferA.order() == bufferB.order()) {
239             for (int i = uintCount; i > 0; i --) {
240                 long va = bufferA.getUnsignedInt(aIndex);
241                 long vb = bufferB.getUnsignedInt(bIndex);
242                 if (va > vb) {
243                     return 1;
244                 }
245                 if (va < vb) {
246                     return -1;
247                 }
248                 aIndex += 4;
249                 bIndex += 4;
250             }
251         } else {
252             for (int i = uintCount; i > 0; i --) {
253                 long va = bufferA.getUnsignedInt(aIndex);
254                 long vb = swapInt(bufferB.getInt(bIndex)) & 0xFFFFFFFFL;
255                 if (va > vb) {
256                     return 1;
257                 }
258                 if (va < vb) {
259                     return -1;
260                 }
261                 aIndex += 4;
262                 bIndex += 4;
263             }
264         }
265 
266         for (int i = byteCount; i > 0; i --) {
267             short va = bufferA.getUnsignedByte(aIndex);
268             short vb = bufferB.getUnsignedByte(bIndex);
269             if (va > vb) {
270                 return 1;
271             }
272             if (va < vb) {
273                 return -1;
274             }
275             aIndex ++;
276             bIndex ++;
277         }
278 
279         return aLen - bLen;
280     }
281 
282     /**
283      * The default implementation of {@link ByteBuf#indexOf(int, int, byte)}.
284      * This method is useful when implementing a new buffer type.
285      */
286     public static int indexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
287         if (fromIndex <= toIndex) {
288             return firstIndexOf(buffer, fromIndex, toIndex, value);
289         } else {
290             return lastIndexOf(buffer, fromIndex, toIndex, value);
291         }
292     }
293 
294     /**
295      * Toggles the endianness of the specified 16-bit short integer.
296      */
297     public static short swapShort(short value) {
298         return Short.reverseBytes(value);
299     }
300 
301     /**
302      * Toggles the endianness of the specified 24-bit medium integer.
303      */
304     public static int swapMedium(int value) {
305         int swapped = value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
306         if ((swapped & 0x800000) != 0) {
307             swapped |= 0xff000000;
308         }
309         return swapped;
310     }
311 
312     /**
313      * Toggles the endianness of the specified 32-bit integer.
314      */
315     public static int swapInt(int value) {
316         return Integer.reverseBytes(value);
317     }
318 
319     /**
320      * Toggles the endianness of the specified 64-bit long integer.
321      */
322     public static long swapLong(long value) {
323         return Long.reverseBytes(value);
324     }
325 
326     /**
327      * Read the given amount of bytes into a new {@link ByteBuf} that is allocated from the {@link ByteBufAllocator}.
328      */
329     public static ByteBuf readBytes(ByteBufAllocator alloc, ByteBuf buffer, int length) {
330         boolean release = true;
331         ByteBuf dst = alloc.buffer(length);
332         try {
333             buffer.readBytes(dst);
334             release = false;
335             return dst;
336         } finally {
337             if (release) {
338                 dst.release();
339             }
340         }
341     }
342 
343     private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
344         fromIndex = Math.max(fromIndex, 0);
345         if (fromIndex >= toIndex || buffer.capacity() == 0) {
346             return -1;
347         }
348 
349         for (int i = fromIndex; i < toIndex; i ++) {
350             if (buffer.getByte(i) == value) {
351                 return i;
352             }
353         }
354 
355         return -1;
356     }
357 
358     private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
359         fromIndex = Math.min(fromIndex, buffer.capacity());
360         if (fromIndex < 0 || buffer.capacity() == 0) {
361             return -1;
362         }
363 
364         for (int i = fromIndex - 1; i >= toIndex; i --) {
365             if (buffer.getByte(i) == value) {
366                 return i;
367             }
368         }
369 
370         return -1;
371     }
372 
373     /**
374      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> and write
375      * it to a {@link ByteBuf}.
376      *
377      * This method returns the actual number of bytes written.
378      */
379     public static int writeUtf8(ByteBuf buf, CharSequence seq) {
380         if (buf == null) {
381             throw new NullPointerException("buf");
382         }
383         if (seq == null) {
384             throw new NullPointerException("seq");
385         }
386         // UTF-8 uses max. 3 bytes per char, so calculate the worst case.
387         final int len = seq.length();
388         final int maxSize = len * 3;
389         buf.ensureWritable(maxSize);
390         if (buf instanceof AbstractByteBuf) {
391             // Fast-Path
392             AbstractByteBuf buffer = (AbstractByteBuf) buf;
393             int oldWriterIndex = buffer.writerIndex;
394             int writerIndex = oldWriterIndex;
395 
396             // We can use the _set methods as these not need to do any index checks and reference checks.
397             // This is possible as we called ensureWritable(...) before.
398             for (int i = 0; i < len; i++) {
399                 char c = seq.charAt(i);
400                 if (c < 0x80) {
401                     buffer._setByte(writerIndex++, (byte) c);
402                 } else if (c < 0x800) {
403                     buffer._setByte(writerIndex++, (byte) (0xc0 | (c >> 6)));
404                     buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
405                 } else {
406                     buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
407                     buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
408                     buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
409                 }
410             }
411             // update the writerIndex without any extra checks for performance reasons
412             buffer.writerIndex = writerIndex;
413             return writerIndex - oldWriterIndex;
414         } else {
415             // Maybe we could also check if we can unwrap() to access the wrapped buffer which
416             // may be an AbstractByteBuf. But this may be overkill so let us keep it simple for now.
417             byte[] bytes = seq.toString().getBytes(CharsetUtil.UTF_8);
418             buf.writeBytes(bytes);
419             return bytes.length;
420         }
421     }
422 
423     /**
424      * Encode a {@link CharSequence} in <a href="http://en.wikipedia.org/wiki/ASCII">ASCII</a> and write it
425      * to a {@link ByteBuf}.
426      *
427      * This method returns the actual number of bytes written.
428      */
429     public static int writeAscii(ByteBuf buf, CharSequence seq) {
430         if (buf == null) {
431             throw new NullPointerException("buf");
432         }
433         if (seq == null) {
434             throw new NullPointerException("seq");
435         }
436         // ASCII uses 1 byte per char
437         final int len = seq.length();
438         buf.ensureWritable(len);
439         if (buf instanceof AbstractByteBuf) {
440             // Fast-Path
441             AbstractByteBuf buffer = (AbstractByteBuf) buf;
442             int writerIndex = buffer.writerIndex;
443 
444             // We can use the _set methods as these not need to do any index checks and reference checks.
445             // This is possible as we called ensureWritable(...) before.
446             for (int i = 0; i < len; i++) {
447                 buffer._setByte(writerIndex++, (byte) seq.charAt(i));
448             }
449             // update the writerIndex without any extra checks for performance reasons
450             buffer.writerIndex = writerIndex;
451         } else {
452             // Maybe we could also check if we can unwrap() to access the wrapped buffer which
453             // may be an AbstractByteBuf. But this may be overkill so let us keep it simple for now.
454             buf.writeBytes(seq.toString().getBytes(CharsetUtil.US_ASCII));
455         }
456         return len;
457     }
458 
459     /**
460      * Encode the given {@link CharBuffer} using the given {@link Charset} into a new {@link ByteBuf} which
461      * is allocated via the {@link ByteBufAllocator}.
462      */
463     public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
464         return encodeString0(alloc, false, src, charset);
465     }
466 
467     static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset) {
468         final CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
469         int length = (int) ((double) src.remaining() * encoder.maxBytesPerChar());
470         boolean release = true;
471         final ByteBuf dst;
472         if (enforceHeap) {
473             dst = alloc.heapBuffer(length);
474         } else {
475             dst = alloc.buffer(length);
476         }
477         try {
478             final ByteBuffer dstBuf = dst.internalNioBuffer(0, length);
479             final int pos = dstBuf.position();
480             CoderResult cr = encoder.encode(src, dstBuf, true);
481             if (!cr.isUnderflow()) {
482                 cr.throwException();
483             }
484             cr = encoder.flush(dstBuf);
485             if (!cr.isUnderflow()) {
486                 cr.throwException();
487             }
488             dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
489             release = false;
490             return dst;
491         } catch (CharacterCodingException x) {
492             throw new IllegalStateException(x);
493         } finally {
494             if (release) {
495                 dst.release();
496             }
497         }
498     }
499 
500     static String decodeString(ByteBuffer src, Charset charset) {
501         final CharsetDecoder decoder = CharsetUtil.getDecoder(charset);
502         final CharBuffer dst = CharBuffer.allocate(
503                 (int) ((double) src.remaining() * decoder.maxCharsPerByte()));
504         try {
505             CoderResult cr = decoder.decode(src, dst, true);
506             if (!cr.isUnderflow()) {
507                 cr.throwException();
508             }
509             cr = decoder.flush(dst);
510             if (!cr.isUnderflow()) {
511                 cr.throwException();
512             }
513         } catch (CharacterCodingException x) {
514             throw new IllegalStateException(x);
515         }
516         return dst.flip().toString();
517     }
518 
519     /**
520      * Returns a cached thread-local direct buffer, if available.
521      *
522      * @return a cached thread-local direct buffer, if available.  {@code null} otherwise.
523      */
524     public static ByteBuf threadLocalDirectBuffer() {
525         if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
526             return null;
527         }
528 
529         if (PlatformDependent.hasUnsafe()) {
530             return ThreadLocalUnsafeDirectByteBuf.newInstance();
531         } else {
532             return ThreadLocalDirectByteBuf.newInstance();
533         }
534     }
535 
536     static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {
537 
538         private static final Recycler<ThreadLocalUnsafeDirectByteBuf> RECYCLER =
539                 new Recycler<ThreadLocalUnsafeDirectByteBuf>() {
540                     @Override
541                     protected ThreadLocalUnsafeDirectByteBuf newObject(Handle handle) {
542                         return new ThreadLocalUnsafeDirectByteBuf(handle);
543                     }
544                 };
545 
546         static ThreadLocalUnsafeDirectByteBuf newInstance() {
547             ThreadLocalUnsafeDirectByteBuf buf = RECYCLER.get();
548             buf.setRefCnt(1);
549             return buf;
550         }
551 
552         private final Handle handle;
553 
554         private ThreadLocalUnsafeDirectByteBuf(Handle handle) {
555             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
556             this.handle = handle;
557         }
558 
559         @Override
560         protected void deallocate() {
561             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
562                 super.deallocate();
563             } else {
564                 clear();
565                 RECYCLER.recycle(this, handle);
566             }
567         }
568     }
569 
570     static final class ThreadLocalDirectByteBuf extends UnpooledDirectByteBuf {
571 
572         private static final Recycler<ThreadLocalDirectByteBuf> RECYCLER = new Recycler<ThreadLocalDirectByteBuf>() {
573             @Override
574             protected ThreadLocalDirectByteBuf newObject(Handle handle) {
575                 return new ThreadLocalDirectByteBuf(handle);
576             }
577         };
578 
579         static ThreadLocalDirectByteBuf newInstance() {
580             ThreadLocalDirectByteBuf buf = RECYCLER.get();
581             buf.setRefCnt(1);
582             return buf;
583         }
584 
585         private final Handle handle;
586 
587         private ThreadLocalDirectByteBuf(Handle handle) {
588             super(UnpooledByteBufAllocator.DEFAULT, 256, Integer.MAX_VALUE);
589             this.handle = handle;
590         }
591 
592         @Override
593         protected void deallocate() {
594             if (capacity() > THREAD_LOCAL_BUFFER_SIZE) {
595                 super.deallocate();
596             } else {
597                 clear();
598                 RECYCLER.recycle(this, handle);
599             }
600         }
601     }
602 
603     private ByteBufUtil() { }
604 }