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 }