1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.RandomAccessFile;
26 import java.nio.Buffer;
27 import java.nio.BufferOverflowException;
28 import java.nio.ByteBuffer;
29 import java.nio.channels.FileChannel;
30 import java.nio.channels.FileChannel.MapMode;
31 import java.nio.charset.Charset;
32 import java.nio.charset.StandardCharsets;
33 import java.util.Arrays;
34
35 import org.eclipse.jetty.util.resource.Resource;
36
37
38 /* ------------------------------------------------------------------------------- */
39 /**
40 * Buffer utility methods.
41 * <p>The standard JVM {@link ByteBuffer} can exist in two modes: In fill mode the valid
42 * data is between 0 and pos; In flush mode the valid data is between the pos and the limit.
43 * The various ByteBuffer methods assume a mode and some of them will switch or enforce a mode:
44 * Allocate and clear set fill mode; flip and compact switch modes; read and write assume fill
45 * and flush modes. This duality can result in confusing code such as:
46 * </p>
47 * <pre>
48 * buffer.clear();
49 * channel.write(buffer);
50 * </pre>
51 * <p>
52 * Which looks as if it should write no data, but in fact writes the buffer worth of garbage.
53 * </p>
54 * <p>
55 * The BufferUtil class provides a set of utilities that operate on the convention that ByteBuffers
56 * will always be left, passed in an API or returned from a method in the flush mode - ie with
57 * valid data between the pos and limit. This convention is adopted so as to avoid confusion as to
58 * what state a buffer is in and to avoid excessive copying of data that can result with the usage
59 * of compress.</p>
60 * <p>
61 * Thus this class provides alternate implementations of {@link #allocate(int)},
62 * {@link #allocateDirect(int)} and {@link #clear(ByteBuffer)} that leave the buffer
63 * in flush mode. Thus the following tests will pass:
64 * </p>
65 * <pre>
66 * ByteBuffer buffer = BufferUtil.allocate(1024);
67 * assert(buffer.remaining()==0);
68 * BufferUtil.clear(buffer);
69 * assert(buffer.remaining()==0);
70 * </pre>
71 * <p>If the BufferUtil methods {@link #fill(ByteBuffer, byte[], int, int)},
72 * {@link #append(ByteBuffer, byte[], int, int)} or {@link #put(ByteBuffer, ByteBuffer)} are used,
73 * then the caller does not need to explicitly switch the buffer to fill mode.
74 * If the caller wishes to use other ByteBuffer bases libraries to fill a buffer,
75 * then they can use explicit calls of #flipToFill(ByteBuffer) and #flipToFlush(ByteBuffer, int)
76 * to change modes. Note because this convention attempts to avoid the copies of compact, the position
77 * is not set to zero on each fill cycle and so its value must be remembered:
78 * </p>
79 * <pre>
80 * int pos = BufferUtil.flipToFill(buffer);
81 * try
82 * {
83 * buffer.put(data);
84 * }
85 * finally
86 * {
87 * flipToFlush(buffer, pos);
88 * }
89 * </pre>
90 * <p>
91 * The flipToFill method will effectively clear the buffer if it is empty and will compact the buffer if there is no space.
92 * </p>
93 */
94 public class BufferUtil
95 {
96 static final int TEMP_BUFFER_SIZE = 4096;
97 static final byte SPACE = 0x20;
98 static final byte MINUS = '-';
99 static final byte[] DIGIT =
100 {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D',
101 (byte)'E', (byte)'F'};
102
103 public static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]);
104
105 /* ------------------------------------------------------------ */
106 /** Allocate ByteBuffer in flush mode.
107 * The position and limit will both be zero, indicating that the buffer is
108 * empty and must be flipped before any data is put to it.
109 * @param capacity capacity of the allocated ByteBuffer
110 * @return Buffer
111 */
112 public static ByteBuffer allocate(int capacity)
113 {
114 ByteBuffer buf = ByteBuffer.allocate(capacity);
115 buf.limit(0);
116 return buf;
117 }
118
119 /* ------------------------------------------------------------ */
120 /** Allocate ByteBuffer in flush mode.
121 * The position and limit will both be zero, indicating that the buffer is
122 * empty and in flush mode.
123 * @param capacity capacity of the allocated ByteBuffer
124 * @return Buffer
125 */
126 public static ByteBuffer allocateDirect(int capacity)
127 {
128 ByteBuffer buf = ByteBuffer.allocateDirect(capacity);
129 buf.limit(0);
130 return buf;
131 }
132
133
134 /* ------------------------------------------------------------ */
135 /** Clear the buffer to be empty in flush mode.
136 * The position and limit are set to 0;
137 * @param buffer The buffer to clear.
138 */
139 public static void clear(ByteBuffer buffer)
140 {
141 if (buffer != null)
142 {
143 buffer.position(0);
144 buffer.limit(0);
145 }
146 }
147
148 /* ------------------------------------------------------------ */
149 /** Clear the buffer to be empty in fill mode.
150 * The position is set to 0 and the limit is set to the capacity.
151 * @param buffer The buffer to clear.
152 */
153 public static void clearToFill(ByteBuffer buffer)
154 {
155 if (buffer != null)
156 {
157 buffer.position(0);
158 buffer.limit(buffer.capacity());
159 }
160 }
161
162 /* ------------------------------------------------------------ */
163 /** Flip the buffer to fill mode.
164 * The position is set to the first unused position in the buffer
165 * (the old limit) and the limit is set to the capacity.
166 * If the buffer is empty, then this call is effectively {@link #clearToFill(ByteBuffer)}.
167 * If there is no unused space to fill, a {@link ByteBuffer#compact()} is done to attempt
168 * to create space.
169 * <p>
170 * This method is used as a replacement to {@link ByteBuffer#compact()}.
171 *
172 * @param buffer The buffer to flip
173 * @return The position of the valid data before the flipped position. This value should be
174 * passed to a subsequent call to {@link #flipToFlush(ByteBuffer, int)}
175 */
176 public static int flipToFill(ByteBuffer buffer)
177 {
178 int position = buffer.position();
179 int limit = buffer.limit();
180 if (position == limit)
181 {
182 buffer.position(0);
183 buffer.limit(buffer.capacity());
184 return 0;
185 }
186
187 int capacity = buffer.capacity();
188 if (limit == capacity)
189 {
190 buffer.compact();
191 return 0;
192 }
193
194 buffer.position(limit);
195 buffer.limit(capacity);
196 return position;
197 }
198
199
200 /* ------------------------------------------------------------ */
201 /** Flip the buffer to Flush mode.
202 * The limit is set to the first unused byte(the old position) and
203 * the position is set to the passed position.
204 * <p>
205 * This method is used as a replacement of {@link Buffer#flip()}.
206 * @param buffer the buffer to be flipped
207 * @param position The position of valid data to flip to. This should
208 * be the return value of the previous call to {@link #flipToFill(ByteBuffer)}
209 */
210 public static void flipToFlush(ByteBuffer buffer, int position)
211 {
212 buffer.limit(buffer.position());
213 buffer.position(position);
214 }
215
216
217 /* ------------------------------------------------------------ */
218 /** Convert a ByteBuffer to a byte array.
219 * @param buffer The buffer to convert in flush mode. The buffer is not altered.
220 * @return An array of bytes duplicated from the buffer.
221 */
222 public static byte[] toArray(ByteBuffer buffer)
223 {
224 if (buffer.hasArray())
225 {
226 byte[] array = buffer.array();
227 int from=buffer.arrayOffset() + buffer.position();
228 return Arrays.copyOfRange(array,from,from+buffer.remaining());
229 }
230 else
231 {
232 byte[] to = new byte[buffer.remaining()];
233 buffer.slice().get(to);
234 return to;
235 }
236 }
237
238 /* ------------------------------------------------------------ */
239 /** Check for an empty or null buffer.
240 * @param buf the buffer to check
241 * @return true if the buffer is null or empty.
242 */
243 public static boolean isEmpty(ByteBuffer buf)
244 {
245 return buf == null || buf.remaining() == 0;
246 }
247
248 /* ------------------------------------------------------------ */
249 /** Check for a non null and non empty buffer.
250 * @param buf the buffer to check
251 * @return true if the buffer is not null and not empty.
252 */
253 public static boolean hasContent(ByteBuffer buf)
254 {
255 return buf != null && buf.remaining() > 0;
256 }
257
258 /* ------------------------------------------------------------ */
259 /** Check for a non null and full buffer.
260 * @param buf the buffer to check
261 * @return true if the buffer is not null and the limit equals the capacity.
262 */
263 public static boolean isFull(ByteBuffer buf)
264 {
265 return buf != null && buf.limit() == buf.capacity();
266 }
267
268 /* ------------------------------------------------------------ */
269 /** Get remaining from null checked buffer
270 * @param buffer The buffer to get the remaining from, in flush mode.
271 * @return 0 if the buffer is null, else the bytes remaining in the buffer.
272 */
273 public static int length(ByteBuffer buffer)
274 {
275 return buffer == null ? 0 : buffer.remaining();
276 }
277
278 /* ------------------------------------------------------------ */
279 /** Get the space from the limit to the capacity
280 * @param buffer the buffer to get the space from
281 * @return space
282 */
283 public static int space(ByteBuffer buffer)
284 {
285 if (buffer == null)
286 return 0;
287 return buffer.capacity() - buffer.limit();
288 }
289
290 /* ------------------------------------------------------------ */
291 /** Compact the buffer
292 * @param buffer the buffer to compact
293 * @return true if the compact made a full buffer have space
294 */
295 public static boolean compact(ByteBuffer buffer)
296 {
297 if (buffer.position()==0)
298 return false;
299 boolean full = buffer.limit() == buffer.capacity();
300 buffer.compact().flip();
301 return full && buffer.limit() < buffer.capacity();
302 }
303
304 /* ------------------------------------------------------------ */
305 /**
306 * Put data from one buffer into another, avoiding over/under flows
307 * @param from Buffer to take bytes from in flush mode
308 * @param to Buffer to put bytes to in fill mode.
309 * @return number of bytes moved
310 */
311 public static int put(ByteBuffer from, ByteBuffer to)
312 {
313 int put;
314 int remaining = from.remaining();
315 if (remaining > 0)
316 {
317 if (remaining <= to.remaining())
318 {
319 to.put(from);
320 put = remaining;
321 from.position(from.limit());
322 }
323 else if (from.hasArray())
324 {
325 put = to.remaining();
326 to.put(from.array(), from.arrayOffset() + from.position(), put);
327 from.position(from.position() + put);
328 }
329 else
330 {
331 put = to.remaining();
332 ByteBuffer slice = from.slice();
333 slice.limit(put);
334 to.put(slice);
335 from.position(from.position() + put);
336 }
337 }
338 else
339 put = 0;
340
341 return put;
342 }
343
344 /* ------------------------------------------------------------ */
345 /**
346 * Put data from one buffer into another, avoiding over/under flows
347 * @param from Buffer to take bytes from in flush mode
348 * @param to Buffer to put bytes to in flush mode. The buffer is flipToFill before the put and flipToFlush after.
349 * @return number of bytes moved
350 * @deprecated use {@link #append(ByteBuffer, ByteBuffer)}
351 */
352 public static int flipPutFlip(ByteBuffer from, ByteBuffer to)
353 {
354 return append(to,from);
355 }
356
357 /* ------------------------------------------------------------ */
358 /** Append bytes to a buffer.
359 * @param to Buffer is flush mode
360 * @param b bytes to append
361 * @param off offset into byte
362 * @param len length to append
363 * @throws BufferOverflowException if unable to append buffer due to space limits
364 */
365 public static void append(ByteBuffer to, byte[] b, int off, int len) throws BufferOverflowException
366 {
367 int pos = flipToFill(to);
368 try
369 {
370 to.put(b, off, len);
371 }
372 finally
373 {
374 flipToFlush(to, pos);
375 }
376 }
377
378 /* ------------------------------------------------------------ */
379 /** Appends a byte to a buffer
380 * @param to Buffer is flush mode
381 * @param b byte to append
382 */
383 public static void append(ByteBuffer to, byte b)
384 {
385 int pos = flipToFill(to);
386 try
387 {
388 to.put(b);
389 }
390 finally
391 {
392 flipToFlush(to, pos);
393 }
394 }
395
396 /* ------------------------------------------------------------ */
397 /** Appends a buffer to a buffer
398 * @param to Buffer is flush mode
399 * @param b buffer to append
400 * @return The position of the valid data before the flipped position.
401 */
402 public static int append(ByteBuffer to, ByteBuffer b)
403 {
404 int pos = flipToFill(to);
405 try
406 {
407 return put(b, to);
408 }
409 finally
410 {
411 flipToFlush(to, pos);
412 }
413 }
414
415 /* ------------------------------------------------------------ */
416 /**
417 * Like append, but does not throw {@link BufferOverflowException}
418 * @param to Buffer is flush mode
419 * @param b bytes to fill
420 * @param off offset into byte
421 * @param len length to fill
422 * @return The position of the valid data before the flipped position.
423 */
424 public static int fill(ByteBuffer to, byte[] b, int off, int len)
425 {
426 int pos = flipToFill(to);
427 try
428 {
429 int remaining = to.remaining();
430 int take = remaining < len ? remaining : len;
431 to.put(b, off, take);
432 return take;
433 }
434 finally
435 {
436 flipToFlush(to, pos);
437 }
438 }
439
440
441 /* ------------------------------------------------------------ */
442 public static void readFrom(File file, ByteBuffer buffer) throws IOException
443 {
444 try(RandomAccessFile raf = new RandomAccessFile(file,"r"))
445 {
446 FileChannel channel = raf.getChannel();
447 long needed=raf.length();
448
449 while (needed>0 && buffer.hasRemaining())
450 needed=needed-channel.read(buffer);
451 }
452 }
453
454 /* ------------------------------------------------------------ */
455 public static void readFrom(InputStream is, int needed, ByteBuffer buffer) throws IOException
456 {
457 ByteBuffer tmp = allocate(8192);
458
459 while (needed > 0 && buffer.hasRemaining())
460 {
461 int l = is.read(tmp.array(), 0, 8192);
462 if (l < 0)
463 break;
464 tmp.position(0);
465 tmp.limit(l);
466 buffer.put(tmp);
467 }
468 }
469
470 /* ------------------------------------------------------------ */
471 public static void writeTo(ByteBuffer buffer, OutputStream out) throws IOException
472 {
473 if (buffer.hasArray())
474 {
475 out.write(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
476 // update buffer position, in way similar to non-array version of writeTo
477 buffer.position(buffer.position() + buffer.remaining());
478 }
479 else
480 {
481 byte[] bytes = new byte[TEMP_BUFFER_SIZE];
482 while(buffer.hasRemaining()){
483 int byteCountToWrite = Math.min(buffer.remaining(), TEMP_BUFFER_SIZE);
484 buffer.get(bytes, 0, byteCountToWrite);
485 out.write(bytes,0 , byteCountToWrite);
486 }
487 }
488 }
489
490 /* ------------------------------------------------------------ */
491 /** Convert the buffer to an ISO-8859-1 String
492 * @param buffer The buffer to convert in flush mode. The buffer is unchanged
493 * @return The buffer as a string.
494 */
495 public static String toString(ByteBuffer buffer)
496 {
497 return toString(buffer, StandardCharsets.ISO_8859_1);
498 }
499
500 /* ------------------------------------------------------------ */
501 /** Convert the buffer to an UTF-8 String
502 * @param buffer The buffer to convert in flush mode. The buffer is unchanged
503 * @return The buffer as a string.
504 */
505 public static String toUTF8String(ByteBuffer buffer)
506 {
507 return toString(buffer, StandardCharsets.UTF_8);
508 }
509
510 /* ------------------------------------------------------------ */
511 /** Convert the buffer to an ISO-8859-1 String
512 * @param buffer The buffer to convert in flush mode. The buffer is unchanged
513 * @param charset The {@link Charset} to use to convert the bytes
514 * @return The buffer as a string.
515 */
516 public static String toString(ByteBuffer buffer, Charset charset)
517 {
518 if (buffer == null)
519 return null;
520 byte[] array = buffer.hasArray() ? buffer.array() : null;
521 if (array == null)
522 {
523 byte[] to = new byte[buffer.remaining()];
524 buffer.slice().get(to);
525 return new String(to, 0, to.length, charset);
526 }
527 return new String(array, buffer.arrayOffset() + buffer.position(), buffer.remaining(), charset);
528 }
529
530 /* ------------------------------------------------------------ */
531 /** Convert a partial buffer to a String.
532 *
533 * @param buffer the buffer to convert
534 * @param position The position in the buffer to start the string from
535 * @param length The length of the buffer
536 * @param charset The {@link Charset} to use to convert the bytes
537 * @return The buffer as a string.
538 */
539 public static String toString(ByteBuffer buffer, int position, int length, Charset charset)
540 {
541 if (buffer == null)
542 return null;
543 byte[] array = buffer.hasArray() ? buffer.array() : null;
544 if (array == null)
545 {
546 ByteBuffer ro = buffer.asReadOnlyBuffer();
547 ro.position(position);
548 ro.limit(position + length);
549 byte[] to = new byte[length];
550 ro.get(to);
551 return new String(to, 0, to.length, charset);
552 }
553 return new String(array, buffer.arrayOffset() + position, length, charset);
554 }
555
556 /* ------------------------------------------------------------ */
557 /**
558 * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
559 *
560 * @param buffer
561 * A buffer containing an integer in flush mode. The position is not changed.
562 * @return an int
563 */
564 public static int toInt(ByteBuffer buffer)
565 {
566 return toInt(buffer,buffer.position(),buffer.remaining());
567 }
568
569 /* ------------------------------------------------------------ */
570 /**
571 * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an
572 * IllegalArgumentException is thrown
573 *
574 * @param buffer
575 * A buffer containing an integer in flush mode. The position is not changed.
576 * @param position
577 * the position in the buffer to start reading from
578 * @param length
579 * the length of the buffer to use for conversion
580 * @return an int of the buffer bytes
581 */
582 public static int toInt(ByteBuffer buffer, int position, int length)
583 {
584 int val = 0;
585 boolean started = false;
586 boolean minus = false;
587
588 int limit = position+length;
589
590 if (length<=0)
591 throw new NumberFormatException(toString(buffer,position,length,StandardCharsets.UTF_8));
592
593 for (int i = position; i < limit; i++)
594 {
595 byte b = buffer.get(i);
596 if (b <= SPACE)
597 {
598 if (started)
599 break;
600 }
601 else if (b >= '0' && b <= '9')
602 {
603 val = val * 10 + (b - '0');
604 started = true;
605 }
606 else if (b == MINUS && !started)
607 {
608 minus = true;
609 }
610 else
611 break;
612 }
613
614 if (started)
615 return minus ? (-val) : val;
616 throw new NumberFormatException(toString(buffer));
617 }
618
619 /* ------------------------------------------------------------ */
620 /**
621 * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
622 *
623 * @param buffer
624 * A buffer containing an integer in flush mode. The position is updated.
625 * @return an int
626 */
627 public static int takeInt(ByteBuffer buffer)
628 {
629 int val = 0;
630 boolean started = false;
631 boolean minus = false;
632 int i;
633 for (i = buffer.position(); i < buffer.limit(); i++)
634 {
635 byte b = buffer.get(i);
636 if (b <= SPACE)
637 {
638 if (started)
639 break;
640 }
641 else if (b >= '0' && b <= '9')
642 {
643 val = val * 10 + (b - '0');
644 started = true;
645 }
646 else if (b == MINUS && !started)
647 {
648 minus = true;
649 }
650 else
651 break;
652 }
653
654 if (started)
655 {
656 buffer.position(i);
657 return minus ? (-val) : val;
658 }
659 throw new NumberFormatException(toString(buffer));
660 }
661
662 /**
663 * Convert buffer to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
664 *
665 * @param buffer
666 * A buffer containing an integer in flush mode. The position is not changed.
667 * @return an int
668 */
669 public static long toLong(ByteBuffer buffer)
670 {
671 long val = 0;
672 boolean started = false;
673 boolean minus = false;
674
675 for (int i = buffer.position(); i < buffer.limit(); i++)
676 {
677 byte b = buffer.get(i);
678 if (b <= SPACE)
679 {
680 if (started)
681 break;
682 }
683 else if (b >= '0' && b <= '9')
684 {
685 val = val * 10L + (b - '0');
686 started = true;
687 }
688 else if (b == MINUS && !started)
689 {
690 minus = true;
691 }
692 else
693 break;
694 }
695
696 if (started)
697 return minus ? (-val) : val;
698 throw new NumberFormatException(toString(buffer));
699 }
700
701 public static void putHexInt(ByteBuffer buffer, int n)
702 {
703 if (n < 0)
704 {
705 buffer.put((byte)'-');
706
707 if (n == Integer.MIN_VALUE)
708 {
709 buffer.put((byte)(0x7f & '8'));
710 buffer.put((byte)(0x7f & '0'));
711 buffer.put((byte)(0x7f & '0'));
712 buffer.put((byte)(0x7f & '0'));
713 buffer.put((byte)(0x7f & '0'));
714 buffer.put((byte)(0x7f & '0'));
715 buffer.put((byte)(0x7f & '0'));
716 buffer.put((byte)(0x7f & '0'));
717
718 return;
719 }
720 n = -n;
721 }
722
723 if (n < 0x10)
724 {
725 buffer.put(DIGIT[n]);
726 }
727 else
728 {
729 boolean started = false;
730 // This assumes constant time int arithmatic
731 for (int hexDivisor : hexDivisors)
732 {
733 if (n < hexDivisor)
734 {
735 if (started)
736 buffer.put((byte)'0');
737 continue;
738 }
739
740 started = true;
741 int d = n / hexDivisor;
742 buffer.put(DIGIT[d]);
743 n = n - d * hexDivisor;
744 }
745 }
746 }
747
748 /* ------------------------------------------------------------ */
749 public static void putDecInt(ByteBuffer buffer, int n)
750 {
751 if (n < 0)
752 {
753 buffer.put((byte)'-');
754
755 if (n == Integer.MIN_VALUE)
756 {
757 buffer.put((byte)'2');
758 n = 147483648;
759 }
760 else
761 n = -n;
762 }
763
764 if (n < 10)
765 {
766 buffer.put(DIGIT[n]);
767 }
768 else
769 {
770 boolean started = false;
771 // This assumes constant time int arithmatic
772 for (int decDivisor : decDivisors)
773 {
774 if (n < decDivisor)
775 {
776 if (started)
777 buffer.put((byte)'0');
778 continue;
779 }
780
781 started = true;
782 int d = n / decDivisor;
783 buffer.put(DIGIT[d]);
784 n = n - d * decDivisor;
785 }
786 }
787 }
788
789 public static void putDecLong(ByteBuffer buffer, long n)
790 {
791 if (n < 0)
792 {
793 buffer.put((byte)'-');
794
795 if (n == Long.MIN_VALUE)
796 {
797 buffer.put((byte)'9');
798 n = 223372036854775808L;
799 }
800 else
801 n = -n;
802 }
803
804 if (n < 10)
805 {
806 buffer.put(DIGIT[(int)n]);
807 }
808 else
809 {
810 boolean started = false;
811 // This assumes constant time int arithmatic
812 for (long aDecDivisorsL : decDivisorsL)
813 {
814 if (n < aDecDivisorsL)
815 {
816 if (started)
817 buffer.put((byte)'0');
818 continue;
819 }
820
821 started = true;
822 long d = n / aDecDivisorsL;
823 buffer.put(DIGIT[(int)d]);
824 n = n - d * aDecDivisorsL;
825 }
826 }
827 }
828
829 public static ByteBuffer toBuffer(int value)
830 {
831 ByteBuffer buf = ByteBuffer.allocate(32);
832 putDecInt(buf, value);
833 return buf;
834 }
835
836 public static ByteBuffer toBuffer(long value)
837 {
838 ByteBuffer buf = ByteBuffer.allocate(32);
839 putDecLong(buf, value);
840 return buf;
841 }
842
843 public static ByteBuffer toBuffer(String s)
844 {
845 return toBuffer(s, StandardCharsets.ISO_8859_1);
846 }
847
848 public static ByteBuffer toBuffer(String s, Charset charset)
849 {
850 if (s == null)
851 return EMPTY_BUFFER;
852 return toBuffer(s.getBytes(charset));
853 }
854
855 /**
856 * Create a new ByteBuffer using provided byte array.
857 *
858 * @param array
859 * the byte array to back buffer with.
860 * @return ByteBuffer with provided byte array, in flush mode
861 */
862 public static ByteBuffer toBuffer(byte[] array)
863 {
864 if (array == null)
865 return EMPTY_BUFFER;
866 return toBuffer(array, 0, array.length);
867 }
868
869 /**
870 * Create a new ByteBuffer using the provided byte array.
871 *
872 * @param array
873 * the byte array to use.
874 * @param offset
875 * the offset within the byte array to use from
876 * @param length
877 * the length in bytes of the array to use
878 * @return ByteBuffer with provided byte array, in flush mode
879 */
880 public static ByteBuffer toBuffer(byte array[], int offset, int length)
881 {
882 if (array == null)
883 return EMPTY_BUFFER;
884 return ByteBuffer.wrap(array, offset, length);
885 }
886
887 public static ByteBuffer toDirectBuffer(String s)
888 {
889 return toDirectBuffer(s, StandardCharsets.ISO_8859_1);
890 }
891
892 public static ByteBuffer toDirectBuffer(String s, Charset charset)
893 {
894 if (s == null)
895 return EMPTY_BUFFER;
896 byte[] bytes = s.getBytes(charset);
897 ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
898 buf.put(bytes);
899 buf.flip();
900 return buf;
901 }
902
903 public static ByteBuffer toMappedBuffer(File file) throws IOException
904 {
905 try (RandomAccessFile raf = new RandomAccessFile(file, "r"))
906 {
907 return raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
908 }
909 }
910
911 public static ByteBuffer toBuffer(Resource resource,boolean direct) throws IOException
912 {
913 int len=(int)resource.length();
914 if (len<0)
915 throw new IllegalArgumentException("invalid resource: "+String.valueOf(resource)+" len="+len);
916
917 ByteBuffer buffer = direct?BufferUtil.allocateDirect(len):BufferUtil.allocate(len);
918
919 int pos=BufferUtil.flipToFill(buffer);
920 if (resource.getFile()!=null)
921 BufferUtil.readFrom(resource.getFile(),buffer);
922 else
923 {
924 try (InputStream is = resource.getInputStream();)
925 {
926 BufferUtil.readFrom(is,len,buffer);
927 }
928 }
929 BufferUtil.flipToFlush(buffer,pos);
930
931 return buffer;
932 }
933
934 public static String toSummaryString(ByteBuffer buffer)
935 {
936 if (buffer == null)
937 return "null";
938 StringBuilder buf = new StringBuilder();
939 buf.append("[p=");
940 buf.append(buffer.position());
941 buf.append(",l=");
942 buf.append(buffer.limit());
943 buf.append(",c=");
944 buf.append(buffer.capacity());
945 buf.append(",r=");
946 buf.append(buffer.remaining());
947 buf.append("]");
948 return buf.toString();
949 }
950
951 public static String toDetailString(ByteBuffer[] buffer)
952 {
953 StringBuilder builder = new StringBuilder();
954 builder.append('[');
955 for (int i = 0; i < buffer.length; i++)
956 {
957 if (i > 0) builder.append(',');
958 builder.append(toDetailString(buffer[i]));
959 }
960 builder.append(']');
961 return builder.toString();
962 }
963
964
965
966 /* ------------------------------------------------------------ */
967 /** Convert Buffer to string ID independent of content
968 */
969 private static void idString(ByteBuffer buffer, StringBuilder out)
970 {
971 out.append(buffer.getClass().getSimpleName());
972 out.append("@");
973 if (buffer.hasArray() && buffer.arrayOffset()==4)
974 {
975 out.append('T');
976 byte[] array = buffer.array();
977 TypeUtil.toHex(array[0],out);
978 TypeUtil.toHex(array[1],out);
979 TypeUtil.toHex(array[2],out);
980 TypeUtil.toHex(array[3],out);
981 }
982 else
983 out.append(Integer.toHexString(System.identityHashCode(buffer)));
984 }
985
986 /* ------------------------------------------------------------ */
987 /** Convert Buffer to string ID independent of content
988 * @param buffer the buffet to generate a string ID from
989 * @return A string showing the buffer ID
990 */
991 public static String toIDString(ByteBuffer buffer)
992 {
993 StringBuilder buf = new StringBuilder();
994 idString(buffer,buf);
995 return buf.toString();
996 }
997
998
999 /* ------------------------------------------------------------ */
1000 /** Convert Buffer to a detail debug string of pointers and content
1001 * @param buffer the buffer to generate a detail string from
1002 * @return A string showing the pointers and content of the buffer
1003 */
1004 public static String toDetailString(ByteBuffer buffer)
1005 {
1006 if (buffer == null)
1007 return "null";
1008
1009 StringBuilder buf = new StringBuilder();
1010 idString(buffer,buf);
1011 buf.append("[p=");
1012 buf.append(buffer.position());
1013 buf.append(",l=");
1014 buf.append(buffer.limit());
1015 buf.append(",c=");
1016 buf.append(buffer.capacity());
1017 buf.append(",r=");
1018 buf.append(buffer.remaining());
1019 buf.append("]={");
1020
1021 appendDebugString(buf,buffer);
1022
1023 buf.append("}");
1024
1025 return buf.toString();
1026 }
1027
1028 private static void appendDebugString(StringBuilder buf,ByteBuffer buffer)
1029 {
1030 for (int i = 0; i < buffer.position(); i++)
1031 {
1032 appendContentChar(buf,buffer.get(i));
1033 if (i == 16 && buffer.position() > 32)
1034 {
1035 buf.append("...");
1036 i = buffer.position() - 16;
1037 }
1038 }
1039 buf.append("<<<");
1040 for (int i = buffer.position(); i < buffer.limit(); i++)
1041 {
1042 appendContentChar(buf,buffer.get(i));
1043 if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32)
1044 {
1045 buf.append("...");
1046 i = buffer.limit() - 16;
1047 }
1048 }
1049 buf.append(">>>");
1050 int limit = buffer.limit();
1051 buffer.limit(buffer.capacity());
1052 for (int i = limit; i < buffer.capacity(); i++)
1053 {
1054 appendContentChar(buf,buffer.get(i));
1055 if (i == limit + 16 && buffer.capacity() > limit + 32)
1056 {
1057 buf.append("...");
1058 i = buffer.capacity() - 16;
1059 }
1060 }
1061 buffer.limit(limit);
1062 }
1063
1064 private static void appendContentChar(StringBuilder buf, byte b)
1065 {
1066 if (b == '\\')
1067 buf.append("\\\\");
1068 else if (b >= ' ')
1069 buf.append((char)b);
1070 else if (b == '\r')
1071 buf.append("\\r");
1072 else if (b == '\n')
1073 buf.append("\\n");
1074 else if (b == '\t')
1075 buf.append("\\t");
1076 else
1077 buf.append("\\x").append(TypeUtil.toHexString(b));
1078 }
1079
1080
1081 /* ------------------------------------------------------------ */
1082 /** Convert buffer to a Hex Summary String.
1083 * @param buffer the buffer to generate a hex byte summary from
1084 * @return A string showing the escaped content of the buffer around the
1085 * position and limit (marked with <<< and >>>)
1086 */
1087 public static String toHexSummary(ByteBuffer buffer)
1088 {
1089 if (buffer == null)
1090 return "null";
1091 StringBuilder buf = new StringBuilder();
1092
1093 buf.append("b[").append(buffer.remaining()).append("]=");
1094 for (int i = buffer.position(); i < buffer.limit(); i++)
1095 {
1096 TypeUtil.toHex(buffer.get(i),buf);
1097 if (i == buffer.position() + 24 && buffer.limit() > buffer.position() + 32)
1098 {
1099 buf.append("...");
1100 i = buffer.limit() - 8;
1101 }
1102 }
1103 return buf.toString();
1104 }
1105
1106
1107 private final static int[] decDivisors =
1108 {1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1109
1110 private final static int[] hexDivisors =
1111 {0x10000000, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x1};
1112
1113 private final static long[] decDivisorsL =
1114 {1000000000000000000L, 100000000000000000L, 10000000000000000L, 1000000000000000L, 100000000000000L, 10000000000000L, 1000000000000L, 100000000000L,
1115 10000000000L, 1000000000L, 100000000L, 10000000L, 1000000L, 100000L, 10000L, 1000L, 100L, 10L, 1L};
1116
1117 public static void putCRLF(ByteBuffer buffer)
1118 {
1119 buffer.put((byte)13);
1120 buffer.put((byte)10);
1121 }
1122
1123 public static boolean isPrefix(ByteBuffer prefix, ByteBuffer buffer)
1124 {
1125 if (prefix.remaining() > buffer.remaining())
1126 return false;
1127 int bi = buffer.position();
1128 for (int i = prefix.position(); i < prefix.limit(); i++)
1129 if (prefix.get(i) != buffer.get(bi++))
1130 return false;
1131 return true;
1132 }
1133
1134 public static ByteBuffer ensureCapacity(ByteBuffer buffer, int capacity)
1135 {
1136 if (buffer==null)
1137 return allocate(capacity);
1138
1139 if (buffer.capacity()>=capacity)
1140 return buffer;
1141
1142 if (buffer.hasArray())
1143 return ByteBuffer.wrap(Arrays.copyOfRange(buffer.array(), buffer.arrayOffset(), buffer.arrayOffset()+capacity),buffer.position(),buffer.remaining());
1144
1145 throw new UnsupportedOperationException();
1146 }
1147
1148
1149
1150 }