001    /*
002     * Copyright (C) 2008  Trustin Heuiseung Lee
003     *
004     * This library is free software; you can redistribute it and/or
005     * modify it under the terms of the GNU Lesser General Public
006     * License as published by the Free Software Foundation; either
007     * version 2.1 of the License, or (at your option) any later version.
008     *
009     * This library is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this library; if not, write to the Free Software
016     * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA
017     */
018    package net.gleamynode.netty.buffer;
019    
020    import java.nio.ByteBuffer;
021    import java.nio.ByteOrder;
022    
023    
024    /**
025     * Creates a new {@link ChannelBuffer} by allocating new space or by wrapping
026     * or copying existing byte arrays.
027     *
028     * <h3>Use static import</h3>
029     * This classes is intended to be used with Java 5 static import statement:
030     *
031     * <pre>
032     * import static net.gleamynode.netty.buffer.ChannelBuffers.*;
033     *
034     * ChannelBuffer heapBuffer = buffer(128);
035     * ChannelBuffer directBuffer = directBuffer(256);
036     * ChannelBuffer dynamicBuffer = dynamicBuffer(512);
037     * ChannelBuffer wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
038     * ChannelBuffer copiedBuffer = copiedBuffer(ByteBuffer.allocate(128));
039     * </pre>
040     *
041     * <h3>Allocating a new buffer</h3>
042     *
043     * Three buffer types are provided out of the box.
044     *
045     * <ul>
046     * <li>{@link #buffer(int)} allocates a new fixed-capacity heap buffer.</li>
047     * <li>{@link #directBuffer(int)} allocates a new fixed-capacity direct buffer.</li>
048     * <li>{@link #dynamicBuffer(int)} allocates a new dynamic-capacity heap
049     *     buffer, whose capacity increases automatically as needed by a write
050     *     operation.</li>
051     * </ul>
052     *
053     * <h3>Creating a wrapped buffer</h3>
054     *
055     * Wrapped buffer is a buffer which is a view of one or more existing
056     * byte arrays or byte buffer.  Any changes in the content of the original
057     * array or buffer will be reflected in the wrapped buffer.  Various wrapper
058     * methods are provided and their name is all {@code wrappedBuffer()}.
059     * You might want to take a look at this method closely if you want to create
060     * a buffer which is composed of more than one array to reduce the number of
061     * memory copy.
062     *
063     * <h3>Creating a copied buffer</h3>
064     *
065     * Copied buffer is a deep copy of one or more existing byte arrays or byte
066     * buffer.  Unlike a wrapped buffer, there's no shared data between the
067     * original arrays and the copied buffer.  Various copy methods are provided
068     * and their name is all {@code copiedBuffer()}.  It's also convenient to
069     * use this operation to merge multiple buffers into one buffer.
070     *
071     * <h3>Miscellaneous utility methods</h3>
072     *
073     * This class also provides various utility methods to help implementation
074     * of a new buffer type, generation of hex dump and swapping an integer's
075     * byte order.
076     *
077     * @author The Netty Project (netty@googlegroups.com)
078     * @author Trustin Lee (trustin@gmail.com)
079     *
080     * @version $Rev$, $Date$
081     *
082     * @apiviz.landmark
083     * @apiviz.has net.gleamynode.netty.buffer.ChannelBuffer oneway - - creates
084     */
085    public class ChannelBuffers {
086    
087        public static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
088        public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
089    
090        private static final char[][] HEXDUMP_TABLE = new char[65536][];
091    
092        static {
093            for (int i = 0; i < 65536; i ++) {
094                HEXDUMP_TABLE[i] = String.format("%04x", i).toCharArray();
095            }
096        }
097    
098        public static ChannelBuffer buffer(int length) {
099            return buffer(BIG_ENDIAN, length);
100        }
101    
102        public static ChannelBuffer buffer(ByteOrder endianness, int length) {
103            if (length == 0) {
104                return ChannelBuffer.EMPTY_BUFFER;
105            }
106            if (endianness == BIG_ENDIAN) {
107                return new BigEndianHeapChannelBuffer(length);
108            } else if (endianness == LITTLE_ENDIAN) {
109                return new LittleEndianHeapChannelBuffer(length);
110            } else {
111                throw new NullPointerException("endianness");
112            }
113        }
114    
115        public static ChannelBuffer directBuffer(int length) {
116            return directBuffer(BIG_ENDIAN, length);
117        }
118    
119        public static ChannelBuffer directBuffer(ByteOrder endianness, int length) {
120            if (length == 0) {
121                return ChannelBuffer.EMPTY_BUFFER;
122            }
123            ChannelBuffer buffer = new ByteBufferBackedChannelBuffer(
124                    ByteBuffer.allocateDirect(length).order(endianness));
125            buffer.clear();
126            return buffer;
127        }
128    
129        public static ChannelBuffer dynamicBuffer() {
130            return dynamicBuffer(BIG_ENDIAN, 256);
131        }
132    
133        public static ChannelBuffer dynamicBuffer(int estimatedLength) {
134            return dynamicBuffer(BIG_ENDIAN, estimatedLength);
135        }
136    
137        public static ChannelBuffer dynamicBuffer(ByteOrder endianness, int estimatedLength) {
138            return new DynamicChannelBuffer(endianness, estimatedLength);
139        }
140    
141        public static ChannelBuffer wrappedBuffer(byte[] array) {
142            return wrappedBuffer(BIG_ENDIAN, array);
143        }
144    
145        public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[] array) {
146            if (array.length == 0) {
147                return ChannelBuffer.EMPTY_BUFFER;
148            }
149            if (endianness == BIG_ENDIAN) {
150                return new BigEndianHeapChannelBuffer(array);
151            } else if (endianness == LITTLE_ENDIAN) {
152                return new LittleEndianHeapChannelBuffer(array);
153            } else {
154                throw new NullPointerException("endianness");
155            }
156        }
157    
158        public static ChannelBuffer wrappedBuffer(byte[] array, int offset, int length) {
159            return wrappedBuffer(BIG_ENDIAN, array, offset, length);
160        }
161    
162        public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[] array, int offset, int length) {
163            if (length == 0) {
164                return ChannelBuffer.EMPTY_BUFFER;
165            }
166            if (offset == 0) {
167                if (length == array.length) {
168                    return wrappedBuffer(endianness, array);
169                } else {
170                    return new TruncatedChannelBuffer(wrappedBuffer(endianness, array), length);
171                }
172            } else {
173                return new SlicedChannelBuffer(wrappedBuffer(endianness, array), offset, length);
174            }
175        }
176    
177        public static ChannelBuffer wrappedBuffer(ByteBuffer buffer) {
178            if (!buffer.hasRemaining()) {
179                return ChannelBuffer.EMPTY_BUFFER;
180            }
181            if (!buffer.isReadOnly() && buffer.hasArray()) {
182                return wrappedBuffer(buffer.array(), buffer.arrayOffset(),buffer.remaining());
183            } else {
184                return new ByteBufferBackedChannelBuffer(buffer);
185            }
186        }
187    
188        public static ChannelBuffer wrappedBuffer(ChannelBuffer buffer) {
189            if (buffer.readable()) {
190                return buffer.slice();
191            } else {
192                return ChannelBuffer.EMPTY_BUFFER;
193            }
194        }
195    
196        public static ChannelBuffer wrappedBuffer(byte[]... arrays) {
197            return wrappedBuffer(BIG_ENDIAN, arrays);
198        }
199    
200        public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[]... arrays) {
201            switch (arrays.length) {
202            case 0:
203                return ChannelBuffer.EMPTY_BUFFER;
204            case 1:
205                return wrappedBuffer(endianness, arrays[0]);
206            }
207            ChannelBuffer[] wrappedBuffers = new ChannelBuffer[arrays.length];
208            for (int i = 0; i < arrays.length; i ++) {
209                wrappedBuffers[i] = wrappedBuffer(endianness, arrays[i]);
210            }
211            return wrappedBuffer(wrappedBuffers);
212        }
213    
214        public static ChannelBuffer wrappedBuffer(ChannelBuffer... buffers) {
215            switch (buffers.length) {
216            case 0:
217                return ChannelBuffer.EMPTY_BUFFER;
218            case 1:
219                return wrappedBuffer(buffers[0]);
220            default:
221                return new CompositeChannelBuffer(buffers);
222            }
223        }
224    
225        public static ChannelBuffer wrappedBuffer(ByteBuffer... buffers) {
226            switch (buffers.length) {
227            case 0:
228                return ChannelBuffer.EMPTY_BUFFER;
229            case 1:
230                return wrappedBuffer(buffers[0]);
231            }
232            ChannelBuffer[] wrappedBuffers = new ChannelBuffer[buffers.length];
233            for (int i = 0; i < buffers.length; i ++) {
234                wrappedBuffers[i] = wrappedBuffer(buffers[i]);
235            }
236            return wrappedBuffer(wrappedBuffers);
237        }
238    
239        public static ChannelBuffer copiedBuffer(byte[] array) {
240            return copiedBuffer(BIG_ENDIAN, array);
241        }
242    
243        public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[] array) {
244            if (array.length == 0) {
245                return ChannelBuffer.EMPTY_BUFFER;
246            }
247            if (endianness == BIG_ENDIAN) {
248                return new BigEndianHeapChannelBuffer(array.clone());
249            } else if (endianness == LITTLE_ENDIAN) {
250                return new LittleEndianHeapChannelBuffer(array.clone());
251            } else {
252                throw new NullPointerException("endianness");
253            }
254        }
255    
256        public static ChannelBuffer copiedBuffer(byte[] array, int offset, int length) {
257            return copiedBuffer(BIG_ENDIAN, array, offset, length);
258        }
259    
260        public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[] array, int offset, int length) {
261            if (length == 0) {
262                return ChannelBuffer.EMPTY_BUFFER;
263            }
264            byte[] copy = new byte[length];
265            System.arraycopy(array, offset, copy, 0, length);
266            return wrappedBuffer(endianness, copy);
267        }
268    
269        public static ChannelBuffer copiedBuffer(ByteBuffer buffer) {
270            int length = buffer.remaining();
271            if (length == 0) {
272                return ChannelBuffer.EMPTY_BUFFER;
273            }
274            byte[] copy = new byte[length];
275            int position = buffer.position();
276            try {
277                buffer.get(copy);
278            } finally {
279                buffer.position(position);
280            }
281            return wrappedBuffer(buffer.order(), copy);
282        }
283    
284        public static ChannelBuffer copiedBuffer(ChannelBuffer buffer) {
285            return buffer.copy();
286        }
287    
288        public static ChannelBuffer copiedBuffer(byte[]... arrays) {
289            return copiedBuffer(BIG_ENDIAN, arrays);
290        }
291    
292        public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[]... arrays) {
293            switch (arrays.length) {
294            case 0:
295                return ChannelBuffer.EMPTY_BUFFER;
296            case 1:
297                return copiedBuffer(endianness, arrays[0]);
298            }
299    
300            // Merge the specified arrays into one array.
301            int length = 0;
302            for (byte[] a: arrays) {
303                if (Integer.MAX_VALUE - length < a.length) {
304                    throw new IllegalArgumentException(
305                            "The total length of the specified arrays is too big.");
306                }
307                length += a.length;
308            }
309    
310            byte[] mergedArray = new byte[length];
311            for (int i = 0, j = 0; i < arrays.length; i ++) {
312                byte[] a = arrays[i];
313                System.arraycopy(a, 0, mergedArray, j, a.length);
314                j += a.length;
315            }
316    
317            return wrappedBuffer(endianness, mergedArray);
318        }
319    
320        public static ChannelBuffer copiedBuffer(ChannelBuffer... buffers) {
321            switch (buffers.length) {
322            case 0:
323                return ChannelBuffer.EMPTY_BUFFER;
324            case 1:
325                return copiedBuffer(buffers[0]);
326            }
327    
328            ChannelBuffer[] copiedBuffers = new ChannelBuffer[buffers.length];
329            for (int i = 0; i < buffers.length; i ++) {
330                copiedBuffers[i] = buffers[i].copy();
331            }
332            return wrappedBuffer(copiedBuffers);
333        }
334    
335        public static ChannelBuffer copiedBuffer(ByteBuffer... buffers) {
336            switch (buffers.length) {
337            case 0:
338                return ChannelBuffer.EMPTY_BUFFER;
339            case 1:
340                return copiedBuffer(buffers[0]);
341            }
342    
343            ChannelBuffer[] copiedBuffers = new ChannelBuffer[buffers.length];
344            for (int i = 0; i < buffers.length; i ++) {
345                copiedBuffers[i] = wrappedBuffer(buffers[i]).copy();
346            }
347            return wrappedBuffer(copiedBuffers);
348        }
349    
350        public static ChannelBuffer unmodifiableBuffer(ChannelBuffer buffer) {
351            if (buffer instanceof ReadOnlyChannelBuffer) {
352                buffer = ((ReadOnlyChannelBuffer) buffer).unwrap();
353            }
354            return new ReadOnlyChannelBuffer(buffer);
355        }
356    
357        public static String hexDump(ChannelBuffer buffer) {
358            return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes());
359        }
360    
361        public static String hexDump(ChannelBuffer buffer, int fromIndex, int length) {
362            if (length < 0) {
363                throw new IllegalArgumentException("length: " + length);
364            }
365            if (length == 0) {
366                return "";
367            }
368    
369            int endIndex = fromIndex + (length >>> 1 << 1);
370            boolean oddLength = length % 2 != 0;
371            char[] buf = new char[length << 1];
372    
373            int srcIdx = fromIndex;
374            int dstIdx = 0;
375            for (; srcIdx < endIndex; srcIdx += 2, dstIdx += 4) {
376                System.arraycopy(
377                        HEXDUMP_TABLE[buffer.getShort(srcIdx) & 0xFFFF],
378                        0, buf, dstIdx, 4);
379            }
380    
381            if (oddLength) {
382                System.arraycopy(
383                        HEXDUMP_TABLE[buffer.getByte(srcIdx) & 0xFF],
384                        2, buf, dstIdx, 2);
385            }
386    
387            return new String(buf);
388        }
389    
390        public static int hashCode(ChannelBuffer buffer) {
391            final int aLen = buffer.readableBytes();
392            final int intCount = aLen >>> 2;
393            final int byteCount = aLen & 3;
394    
395            int hashCode = 1;
396            int arrayIndex = buffer.readerIndex();
397            for (int i = intCount; i > 0; i --) {
398                hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
399                arrayIndex += 4;
400            }
401            for (int i = byteCount; i > 0; i --) {
402                hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
403            }
404    
405            if (hashCode == 0) {
406                hashCode = 1;
407            }
408            return hashCode;
409        }
410    
411        public static boolean equals(ChannelBuffer bufferA, ChannelBuffer bufferB) {
412            final int aLen = bufferA.readableBytes();
413            if (aLen != bufferB.readableBytes()) {
414                return false;
415            }
416    
417            final int longCount = aLen >>> 3;
418            final int byteCount = aLen & 7;
419    
420            int aIndex = bufferA.readerIndex();
421            int bIndex = bufferB.readerIndex();
422            for (int i = longCount; i > 0; i --) {
423                if (bufferA.getLong(aIndex) != bufferB.getLong(bIndex)) {
424                    return false;
425                }
426                aIndex += 8;
427                bIndex += 8;
428            }
429    
430            for (int i = byteCount; i > 0; i --) {
431                if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) {
432                    return false;
433                }
434                aIndex ++;
435                bIndex ++;
436            }
437    
438            return true;
439        }
440    
441        public static int compare(ChannelBuffer bufferA, ChannelBuffer bufferB) {
442            final int aLen = bufferA.readableBytes();
443            final int bLen = bufferB.readableBytes();
444            final int minLength = Math.min(aLen, bLen);
445            final int longCount = minLength >>> 3;
446            final int byteCount = minLength & 7;
447    
448            int aIndex = bufferA.readerIndex();
449            int bIndex = bufferB.readerIndex();
450            for (int i = longCount; i > 0; i --) {
451                long va = bufferA.getLong(aIndex);
452                long vb = bufferB.getLong(bIndex);
453                if (va > vb) {
454                    return 1;
455                } else if (va < vb) {
456                    return -1;
457                }
458                aIndex += 8;
459                bIndex += 8;
460            }
461    
462            for (int i = byteCount; i > 0; i --) {
463                byte va = bufferA.getByte(aIndex);
464                byte vb = bufferB.getByte(bIndex);
465                if (va > vb) {
466                    return 1;
467                } else if (va < vb) {
468                    return -1;
469                }
470                aIndex ++;
471                bIndex ++;
472            }
473    
474            return aLen - bLen;
475        }
476    
477        public static int indexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) {
478            if (fromIndex <= toIndex) {
479                return firstIndexOf(buffer, fromIndex, toIndex, value);
480            } else {
481                return lastIndexOf(buffer, fromIndex, toIndex, value);
482            }
483        }
484    
485        public static int indexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) {
486            if (fromIndex <= toIndex) {
487                return firstIndexOf(buffer, fromIndex, toIndex, indexFinder);
488            } else {
489                return lastIndexOf(buffer, fromIndex, toIndex, indexFinder);
490            }
491        }
492    
493        public static short swapShort(short value) {
494            return (short) (value << 8 | value >>> 8 & 0xff);
495        }
496    
497        public static int swapMedium(int value) {
498            return value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff;
499        }
500    
501        public static int swapInt(int value) {
502            return swapShort((short) value) <<  16 |
503                   swapShort((short) (value >>> 16)) & 0xffff;
504        }
505    
506        public static long swapLong(long value) {
507            return (long) swapInt((int) value) <<  32 |
508                          swapInt((int) (value >>> 32)) & 0xffffffffL;
509        }
510    
511        private static int firstIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) {
512            fromIndex = Math.max(fromIndex, 0);
513            if (fromIndex >= toIndex || buffer.capacity() == 0) {
514                return -1;
515            }
516    
517            for (int i = fromIndex; i < toIndex; i ++) {
518                if (buffer.getByte(i) == value) {
519                    return i;
520                }
521            }
522    
523            return -1;
524        }
525    
526        private static int lastIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) {
527            fromIndex = Math.min(fromIndex, buffer.capacity());
528            if (fromIndex < 0 || buffer.capacity() == 0) {
529                return -1;
530            }
531    
532            for (int i = fromIndex - 1; i >= toIndex; i --) {
533                if (buffer.getByte(i) == value) {
534                    return i;
535                }
536            }
537    
538            return -1;
539        }
540    
541        private static int firstIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) {
542            fromIndex = Math.max(fromIndex, 0);
543            if (fromIndex >= toIndex || buffer.capacity() == 0) {
544                return -1;
545            }
546    
547            for (int i = fromIndex; i < toIndex; i ++) {
548                if (indexFinder.find(buffer, i)) {
549                    return i;
550                }
551            }
552    
553            return -1;
554        }
555    
556        private static int lastIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) {
557            fromIndex = Math.min(fromIndex, buffer.capacity());
558            if (fromIndex < 0 || buffer.capacity() == 0) {
559                return -1;
560            }
561    
562            for (int i = fromIndex - 1; i >= toIndex; i --) {
563                if (indexFinder.find(buffer, i)) {
564                    return i;
565                }
566            }
567    
568            return -1;
569        }
570    
571        private ChannelBuffers() {
572            // Unused
573        }
574    }