View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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  
34  
35  /* ------------------------------------------------------------------------------- */
36  /**
37   * Buffer utility methods.
38   * <p>The standard JVM {@link ByteBuffer} can exist in two modes: In fill mode the valid
39   * data is between 0 and pos; In flush mode the valid data is between the pos and the limit.
40   * The various ByteBuffer methods assume a mode and some of them will switch or enforce a mode:
41   * Allocate and clear set fill mode; flip and compact switch modes; read and write assume fill 
42   * and flush modes.    This duality can result in confusing code such as:
43   * <pre>
44   *     buffer.clear();
45   *     channel.write(buffer);
46   * </pre>
47   * Which looks as if it should write no data, but in fact writes the buffer worth of garbage.
48   * </p>
49   * <p>
50   * The BufferUtil class provides a set of utilities that operate on the convention that ByteBuffers
51   * will always be left, passed in an API or returned from a method in the flush mode - ie with
52   * valid data between the pos and limit.    This convention is adopted so as to avoid confusion as to
53   * what state a buffer is in and to avoid excessive copying of data that can result with the usage 
54   * of compress.</p> 
55   * <p>
56   * Thus this class provides alternate implementations of {@link #allocate(int)}, 
57   * {@link #allocateDirect(int)} and {@link #clear(ByteBuffer)} that leave the buffer
58   * in flush mode.   Thus the following tests will pass:<pre>
59   *     ByteBuffer buffer = BufferUtil.allocate(1024);
60   *     assert(buffer.remaining()==0);
61   *     BufferUtil.clear(buffer);
62   *     assert(buffer.remaining()==0);
63   * </pre>
64   * </p>
65   * <p>If the BufferUtil methods {@link #fill(ByteBuffer, byte[], int, int)}, 
66   * {@link #append(ByteBuffer, byte[], int, int)} or {@link #put(ByteBuffer, ByteBuffer)} are used,
67   * then the caller does not need to explicitly switch the buffer to fill mode.    
68   * If the caller wishes to use other ByteBuffer bases libraries to fill a buffer, 
69   * then they can use explicit calls of #flipToFill(ByteBuffer) and #flipToFlush(ByteBuffer, int)
70   * to change modes.  Note because this convention attempts to avoid the copies of compact, the position
71   * is not set to zero on each fill cycle and so its value must be remembered:
72   * <pre>
73   *      int pos = BufferUtil.flipToFill(buffer);
74   *      try
75   *      {
76   *          buffer.put(data);
77   *      }
78   *      finally
79   *      {
80   *          flipToFlush(buffer, pos);
81   *      }
82   * </pre>
83   * The flipToFill method will effectively clear the buffer if it is emtpy and will compact the buffer if there is no space.
84   * 
85   */
86  public class BufferUtil
87  {
88      static final int TEMP_BUFFER_SIZE = 4096;
89      static final byte SPACE = 0x20;
90      static final byte MINUS = '-';
91      static final byte[] DIGIT =
92              {(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',
93                      (byte)'E', (byte)'F'};
94  
95      public static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]);
96  
97      /* ------------------------------------------------------------ */
98      /** Allocate ByteBuffer in flush mode.
99       * The position and limit will both be zero, indicating that the buffer is
100      * empty and must be flipped before any data is put to it.
101      * @param capacity capacity of the allocated ByteBuffer
102      * @return Buffer
103      */
104     public static ByteBuffer allocate(int capacity)
105     {
106         ByteBuffer buf = ByteBuffer.allocate(capacity);
107         buf.limit(0);
108         return buf;
109     }
110 
111     /* ------------------------------------------------------------ */
112     /** Allocate ByteBuffer in flush mode.
113      * The position and limit will both be zero, indicating that the buffer is
114      * empty and in flush mode.
115      * @param capacity capacity of the allocated ByteBuffer
116      * @return Buffer
117      */
118     public static ByteBuffer allocateDirect(int capacity)
119     {
120         ByteBuffer buf = ByteBuffer.allocateDirect(capacity);
121         buf.limit(0);
122         return buf;
123     }
124 
125 
126     /* ------------------------------------------------------------ */
127     /** Clear the buffer to be empty in flush mode.
128      * The position and limit are set to 0;
129      * @param buffer The buffer to clear.
130      */
131     public static void clear(ByteBuffer buffer)
132     {
133         if (buffer != null)
134         {
135             buffer.position(0);
136             buffer.limit(0);
137         }
138     }
139 
140     /* ------------------------------------------------------------ */
141     /** Clear the buffer to be empty in fill mode.
142      * The position is set to 0 and the limit is set to the capacity.
143      * @param buffer The buffer to clear.
144      */
145     public static void clearToFill(ByteBuffer buffer)
146     {
147         if (buffer != null)
148         {
149             buffer.position(0);
150             buffer.limit(buffer.capacity());
151         }
152     }
153 
154     /* ------------------------------------------------------------ */
155     /** Flip the buffer to fill mode.
156      * The position is set to the first unused position in the buffer
157      * (the old limit) and the limit is set to the capacity.
158      * If the buffer is empty, then this call is effectively {@link #clearToFill(ByteBuffer)}.
159      * If there is no unused space to fill, a {@link ByteBuffer#compact()} is done to attempt
160      * to create space.
161      * <p>
162      * This method is used as a replacement to {@link ByteBuffer#compact()}.
163      *
164      * @param buffer The buffer to flip
165      * @return The position of the valid data before the flipped position. This value should be
166      * passed to a subsequent call to {@link #flipToFlush(ByteBuffer, int)}
167      */
168     public static int flipToFill(ByteBuffer buffer)
169     {
170         int position = buffer.position();
171         int limit = buffer.limit();
172         if (position == limit)
173         {
174             buffer.position(0);
175             buffer.limit(buffer.capacity());
176             return 0;
177         }
178 
179         int capacity = buffer.capacity();
180         if (limit == capacity)
181         {
182             buffer.compact();
183             return 0;
184         }
185 
186         buffer.position(limit);
187         buffer.limit(capacity);
188         return position;
189     }
190 
191 
192     /* ------------------------------------------------------------ */
193     /** Flip the buffer to Flush mode.
194      * The limit is set to the first unused byte(the old position) and
195      * the position is set to the passed position.
196      * <p>
197      * This method is used as a replacement of {@link Buffer#flip()}.
198      * @param buffer   the buffer to be flipped
199      * @param position The position of valid data to flip to. This should
200      * be the return value of the previous call to {@link #flipToFill(ByteBuffer)}
201      */
202     public static void flipToFlush(ByteBuffer buffer, int position)
203     {
204         buffer.limit(buffer.position());
205         buffer.position(position);
206     }
207 
208 
209     /* ------------------------------------------------------------ */
210     /** Convert a ByteBuffer to a byte array.
211      * @param buffer The buffer to convert in flush mode. The buffer is not altered.
212      * @return An array of bytes duplicated from the buffer.
213      */
214     public static byte[] toArray(ByteBuffer buffer)
215     {
216         byte[] to = new byte[buffer.remaining()];
217         if (buffer.hasArray())
218         {
219             byte[] array = buffer.array();
220             System.arraycopy(array, buffer.arrayOffset() + buffer.position(), to, 0, to.length);
221         }
222         else
223             buffer.slice().get(to);
224         return to;
225     }
226 
227     /* ------------------------------------------------------------ */
228     /** Check for an empty or null buffer.
229      * @param buf the buffer to check
230      * @return true if the buffer is null or empty.
231      */
232     public static boolean isEmpty(ByteBuffer buf)
233     {
234         return buf == null || buf.remaining() == 0;
235     }
236 
237     /* ------------------------------------------------------------ */
238     /** Check for a non null and non empty buffer.
239      * @param buf the buffer to check
240      * @return true if the buffer is not null and not empty.
241      */
242     public static boolean hasContent(ByteBuffer buf)
243     {
244         return buf != null && buf.remaining() > 0;
245     }
246 
247     /* ------------------------------------------------------------ */
248     /** Check for a non null and full buffer.
249      * @param buf the buffer to check
250      * @return true if the buffer is not null and the limit equals the capacity.
251      */
252     public static boolean isFull(ByteBuffer buf)
253     {
254         return buf != null && buf.limit() == buf.capacity();
255     }
256 
257     /* ------------------------------------------------------------ */
258     /** Get remaining from null checked buffer
259      * @param buffer The buffer to get the remaining from, in flush mode.
260      * @return 0 if the buffer is null, else the bytes remaining in the buffer.
261      */
262     public static int length(ByteBuffer buffer)
263     {
264         return buffer == null ? 0 : buffer.remaining();
265     }
266 
267     /* ------------------------------------------------------------ */
268     /** Get the space from the limit to the capacity
269      * @param buffer the buffer to get the space from
270      * @return space
271      */
272     public static int space(ByteBuffer buffer)
273     {
274         if (buffer == null)
275             return 0;
276         return buffer.capacity() - buffer.limit();
277     }
278 
279     /* ------------------------------------------------------------ */
280     /** Compact the buffer
281      * @param buffer the buffer to compact
282      * @return true if the compact made a full buffer have space
283      */
284     public static boolean compact(ByteBuffer buffer)
285     {
286         boolean full = buffer.limit() == buffer.capacity();
287         buffer.compact().flip();
288         return full && buffer.limit() < buffer.capacity();
289     }
290 
291     /* ------------------------------------------------------------ */
292     /**
293      * Put data from one buffer into another, avoiding over/under flows
294      * @param from Buffer to take bytes from in flush mode
295      * @param to   Buffer to put bytes to in fill mode.
296      * @return number of bytes moved
297      */
298     public static int put(ByteBuffer from, ByteBuffer to)
299     {
300         int put;
301         int remaining = from.remaining();
302         if (remaining > 0)
303         {
304             if (remaining <= to.remaining())
305             {
306                 to.put(from);
307                 put = remaining;
308                 from.position(0);
309                 from.limit(0);
310             }
311             else if (from.hasArray())
312             {
313                 put = to.remaining();
314                 to.put(from.array(), from.arrayOffset() + from.position(), put);
315                 from.position(from.position() + put);
316             }
317             else
318             {
319                 put = to.remaining();
320                 ByteBuffer slice = from.slice();
321                 slice.limit(put);
322                 to.put(slice);
323                 from.position(from.position() + put);
324             }
325         }
326         else
327             put = 0;
328 
329         return put;
330     }
331 
332     /* ------------------------------------------------------------ */
333     /**
334      * Put data from one buffer into another, avoiding over/under flows
335      * @param from Buffer to take bytes from in flush mode
336      * @param to   Buffer to put bytes to in flush mode. The buffer is flipToFill before the put and flipToFlush after.
337      * @return number of bytes moved
338      */
339     public static int flipPutFlip(ByteBuffer from, ByteBuffer to)
340     {
341         int pos = flipToFill(to);
342         try
343         {
344             return put(from, to);
345         }
346         finally
347         {
348             flipToFlush(to, pos);
349         }
350     }
351 
352     /* ------------------------------------------------------------ */
353     /** Append bytes to a buffer.
354      * 
355      */
356     public static void append(ByteBuffer to, byte[] b, int off, int len) throws BufferOverflowException
357     {
358         int pos = flipToFill(to);
359         try
360         {
361             to.put(b, off, len);
362         }
363         finally
364         {
365             flipToFlush(to, pos);
366         }
367     }
368 
369     /* ------------------------------------------------------------ */
370     /** Appends a byte to a buffer
371      */
372     public static void append(ByteBuffer to, byte b)
373     {
374         int pos = flipToFill(to);
375         try
376         {
377             to.put(b);
378         }
379         finally
380         {
381             flipToFlush(to, pos);
382         }
383     }
384 
385     /* ------------------------------------------------------------ */
386     /**
387      * Like append, but does not throw {@link BufferOverflowException}
388      */
389     public static int fill(ByteBuffer to, byte[] b, int off, int len)
390     {
391         int pos = flipToFill(to);
392         try
393         {
394             int remaining = to.remaining();
395             int take = remaining < len ? remaining : len;
396             to.put(b, off, take);
397             return take;
398         }
399         finally
400         {
401             flipToFlush(to, pos);
402         }
403     }
404 
405 
406     /* ------------------------------------------------------------ */
407     public static void readFrom(File file, ByteBuffer buffer) throws IOException
408     {
409         try(RandomAccessFile raf = new RandomAccessFile(file,"r"))
410         {
411             FileChannel channel = raf.getChannel();
412             long needed=raf.length();
413 
414             while (needed>0 && buffer.hasRemaining())
415                 needed=needed-channel.read(buffer);
416         }
417     }
418 
419     /* ------------------------------------------------------------ */
420     public static void readFrom(InputStream is, int needed, ByteBuffer buffer) throws IOException
421     {
422         ByteBuffer tmp = allocate(8192);
423 
424         while (needed > 0 && buffer.hasRemaining())
425         {
426             int l = is.read(tmp.array(), 0, 8192);
427             if (l < 0)
428                 break;
429             tmp.position(0);
430             tmp.limit(l);
431             buffer.put(tmp);
432         }
433     }
434 
435     /* ------------------------------------------------------------ */
436     public static void writeTo(ByteBuffer buffer, OutputStream out) throws IOException
437     {
438         if (buffer.hasArray())
439             out.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
440         else
441         {
442             byte[] bytes = new byte[TEMP_BUFFER_SIZE];
443             while(buffer.hasRemaining()){
444                 int byteCountToWrite = Math.min(buffer.remaining(), TEMP_BUFFER_SIZE);
445                 buffer.get(bytes, 0, byteCountToWrite);
446                 out.write(bytes,0 , byteCountToWrite);
447             }
448         }
449     }
450 
451     /* ------------------------------------------------------------ */
452     /** Convert the buffer to an ISO-8859-1 String
453      * @param buffer The buffer to convert in flush mode. The buffer is unchanged
454      * @return The buffer as a string.
455      */
456     public static String toString(ByteBuffer buffer)
457     {
458         return toString(buffer, StandardCharsets.ISO_8859_1);
459     }
460 
461     /* ------------------------------------------------------------ */
462     /** Convert the buffer to an UTF-8 String
463      * @param buffer The buffer to convert in flush mode. The buffer is unchanged
464      * @return The buffer as a string.
465      */
466     public static String toUTF8String(ByteBuffer buffer)
467     {
468         return toString(buffer, StandardCharsets.UTF_8);
469     }
470 
471     /* ------------------------------------------------------------ */
472     /** Convert the buffer to an ISO-8859-1 String
473      * @param buffer  The buffer to convert in flush mode. The buffer is unchanged
474      * @param charset The {@link Charset} to use to convert the bytes
475      * @return The buffer as a string.
476      */
477     public static String toString(ByteBuffer buffer, Charset charset)
478     {
479         if (buffer == null)
480             return null;
481         byte[] array = buffer.hasArray() ? buffer.array() : null;
482         if (array == null)
483         {
484             byte[] to = new byte[buffer.remaining()];
485             buffer.slice().get(to);
486             return new String(to, 0, to.length, charset);
487         }
488         return new String(array, buffer.arrayOffset() + buffer.position(), buffer.remaining(), charset);
489     }
490 
491     /* ------------------------------------------------------------ */
492     /** Convert a partial buffer to an ISO-8859-1 String
493      * @param buffer  The buffer to convert in flush mode. The buffer is unchanged
494      * @param charset The {@link Charset} to use to convert the bytes
495      * @return The buffer as a string.
496      */
497     public static String toString(ByteBuffer buffer, int position, int length, Charset charset)
498     {
499         if (buffer == null)
500             return null;
501         byte[] array = buffer.hasArray() ? buffer.array() : null;
502         if (array == null)
503         {
504             ByteBuffer ro = buffer.asReadOnlyBuffer();
505             ro.position(position);
506             ro.limit(position + length);
507             byte[] to = new byte[length];
508             ro.get(to);
509             return new String(to, 0, to.length, charset);
510         }
511         return new String(array, buffer.arrayOffset() + position, length, charset);
512     }
513 
514     /* ------------------------------------------------------------ */
515     /**
516      * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
517      *
518      * @param buffer
519      *            A buffer containing an integer in flush mode. The position is not changed.
520      * @return an int
521      */
522     public static int toInt(ByteBuffer buffer)
523     {
524         int val = 0;
525         boolean started = false;
526         boolean minus = false;
527 
528         for (int i = buffer.position(); i < buffer.limit(); i++)
529         {
530             byte b = buffer.get(i);
531             if (b <= SPACE)
532             {
533                 if (started)
534                     break;
535             }
536             else if (b >= '0' && b <= '9')
537             {
538                 val = val * 10 + (b - '0');
539                 started = true;
540             }
541             else if (b == MINUS && !started)
542             {
543                 minus = true;
544             }
545             else
546                 break;
547         }
548 
549         if (started)
550             return minus ? (-val) : val;
551         throw new NumberFormatException(toString(buffer));
552     }
553 
554     /**
555      * Convert buffer to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
556      *
557      * @param buffer
558      *            A buffer containing an integer in flush mode. The position is not changed.
559      * @return an int
560      */
561     public static long toLong(ByteBuffer buffer)
562     {
563         long val = 0;
564         boolean started = false;
565         boolean minus = false;
566 
567         for (int i = buffer.position(); i < buffer.limit(); i++)
568         {
569             byte b = buffer.get(i);
570             if (b <= SPACE)
571             {
572                 if (started)
573                     break;
574             }
575             else if (b >= '0' && b <= '9')
576             {
577                 val = val * 10L + (b - '0');
578                 started = true;
579             }
580             else if (b == MINUS && !started)
581             {
582                 minus = true;
583             }
584             else
585                 break;
586         }
587 
588         if (started)
589             return minus ? (-val) : val;
590         throw new NumberFormatException(toString(buffer));
591     }
592 
593     public static void putHexInt(ByteBuffer buffer, int n)
594     {
595         if (n < 0)
596         {
597             buffer.put((byte)'-');
598 
599             if (n == Integer.MIN_VALUE)
600             {
601                 buffer.put((byte)(0x7f & '8'));
602                 buffer.put((byte)(0x7f & '0'));
603                 buffer.put((byte)(0x7f & '0'));
604                 buffer.put((byte)(0x7f & '0'));
605                 buffer.put((byte)(0x7f & '0'));
606                 buffer.put((byte)(0x7f & '0'));
607                 buffer.put((byte)(0x7f & '0'));
608                 buffer.put((byte)(0x7f & '0'));
609 
610                 return;
611             }
612             n = -n;
613         }
614 
615         if (n < 0x10)
616         {
617             buffer.put(DIGIT[n]);
618         }
619         else
620         {
621             boolean started = false;
622             // This assumes constant time int arithmatic
623             for (int hexDivisor : hexDivisors)
624             {
625                 if (n < hexDivisor)
626                 {
627                     if (started)
628                         buffer.put((byte)'0');
629                     continue;
630                 }
631 
632                 started = true;
633                 int d = n / hexDivisor;
634                 buffer.put(DIGIT[d]);
635                 n = n - d * hexDivisor;
636             }
637         }
638     }
639 
640     /* ------------------------------------------------------------ */
641     public static void putDecInt(ByteBuffer buffer, int n)
642     {
643         if (n < 0)
644         {
645             buffer.put((byte)'-');
646 
647             if (n == Integer.MIN_VALUE)
648             {
649                 buffer.put((byte)'2');
650                 n = 147483648;
651             }
652             else
653                 n = -n;
654         }
655 
656         if (n < 10)
657         {
658             buffer.put(DIGIT[n]);
659         }
660         else
661         {
662             boolean started = false;
663             // This assumes constant time int arithmatic
664             for (int decDivisor : decDivisors)
665             {
666                 if (n < decDivisor)
667                 {
668                     if (started)
669                         buffer.put((byte)'0');
670                     continue;
671                 }
672 
673                 started = true;
674                 int d = n / decDivisor;
675                 buffer.put(DIGIT[d]);
676                 n = n - d * decDivisor;
677             }
678         }
679     }
680 
681     public static void putDecLong(ByteBuffer buffer, long n)
682     {
683         if (n < 0)
684         {
685             buffer.put((byte)'-');
686 
687             if (n == Long.MIN_VALUE)
688             {
689                 buffer.put((byte)'9');
690                 n = 223372036854775808L;
691             }
692             else
693                 n = -n;
694         }
695 
696         if (n < 10)
697         {
698             buffer.put(DIGIT[(int)n]);
699         }
700         else
701         {
702             boolean started = false;
703             // This assumes constant time int arithmatic
704             for (long aDecDivisorsL : decDivisorsL)
705             {
706                 if (n < aDecDivisorsL)
707                 {
708                     if (started)
709                         buffer.put((byte)'0');
710                     continue;
711                 }
712 
713                 started = true;
714                 long d = n / aDecDivisorsL;
715                 buffer.put(DIGIT[(int)d]);
716                 n = n - d * aDecDivisorsL;
717             }
718         }
719     }
720 
721     public static ByteBuffer toBuffer(int value)
722     {
723         ByteBuffer buf = ByteBuffer.allocate(32);
724         putDecInt(buf, value);
725         return buf;
726     }
727 
728     public static ByteBuffer toBuffer(long value)
729     {
730         ByteBuffer buf = ByteBuffer.allocate(32);
731         putDecLong(buf, value);
732         return buf;
733     }
734 
735     public static ByteBuffer toBuffer(String s)
736     {
737         return ByteBuffer.wrap(s.getBytes(StandardCharsets.ISO_8859_1));
738     }
739 
740     public static ByteBuffer toDirectBuffer(String s)
741     {
742         byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1);
743         ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
744         buf.put(bytes);
745         buf.flip();
746         return buf;
747     }
748 
749     public static ByteBuffer toBuffer(String s, Charset charset)
750     {
751         return ByteBuffer.wrap(s.getBytes(charset));
752     }
753 
754     public static ByteBuffer toDirectBuffer(String s, Charset charset)
755     {
756         byte[] bytes = s.getBytes(charset);
757         ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
758         buf.put(bytes);
759         buf.flip();
760         return buf;
761     }
762 
763     /**
764      * Create a new ByteBuffer using provided byte array.
765      *
766      * @param array
767      *            the byte array to back buffer with.
768      * @return ByteBuffer with provided byte array, in flush mode
769      */
770     public static ByteBuffer toBuffer(byte array[])
771     {
772         return ByteBuffer.wrap(array);
773     }
774 
775     /**
776      * Create a new ByteBuffer using the provided byte array.
777      *
778      * @param array
779      *            the byte array to use.
780      * @param offset
781      *            the offset within the byte array to use from
782      * @param length
783      *            the length in bytes of the array to use
784      * @return ByteBuffer with provided byte array, in flush mode
785      */
786     public static ByteBuffer toBuffer(byte array[], int offset, int length)
787     {
788         return ByteBuffer.wrap(array, offset, length);
789     }
790 
791     public static ByteBuffer toBuffer(File file) throws IOException
792     {
793         try (RandomAccessFile raf = new RandomAccessFile(file, "r"))
794         {
795             return raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
796         }
797     }
798 
799     public static String toSummaryString(ByteBuffer buffer)
800     {
801         if (buffer == null)
802             return "null";
803         StringBuilder buf = new StringBuilder();
804         buf.append("[p=");
805         buf.append(buffer.position());
806         buf.append(",l=");
807         buf.append(buffer.limit());
808         buf.append(",c=");
809         buf.append(buffer.capacity());
810         buf.append(",r=");
811         buf.append(buffer.remaining());
812         buf.append("]");
813         return buf.toString();
814     }
815 
816     public static String toDetailString(ByteBuffer[] buffer)
817     {
818         StringBuilder builder = new StringBuilder();
819         builder.append('[');
820         for (int i = 0; i < buffer.length; i++)
821         {
822             if (i > 0) builder.append(',');
823             builder.append(toDetailString(buffer[i]));
824         }
825         builder.append(']');
826         return builder.toString();
827     }
828 
829     public static String toDetailString(ByteBuffer buffer)
830     {
831         if (buffer == null)
832             return "null";
833 
834         StringBuilder buf = new StringBuilder();
835         buf.append(buffer.getClass().getSimpleName());
836         buf.append("@");
837         if (buffer.hasArray())
838             buf.append(Integer.toHexString(((Object)buffer.array()).hashCode()));
839         else
840             buf.append(Integer.toHexString(buf.hashCode()));
841         buf.append("[p=");
842         buf.append(buffer.position());
843         buf.append(",l=");
844         buf.append(buffer.limit());
845         buf.append(",c=");
846         buf.append(buffer.capacity());
847         buf.append(",r=");
848         buf.append(buffer.remaining());
849         buf.append("]={");
850 
851         for (int i = 0; i < buffer.position(); i++)
852         {
853             char c = (char)buffer.get(i);
854             if (c >= ' ' && c <= 127)
855                 buf.append(c);
856             else if (c == '\r' || c == '\n')
857                 buf.append('|');
858             else
859                 buf.append('\ufffd');
860             if (i == 16 && buffer.position() > 32)
861             {
862                 buf.append("...");
863                 i = buffer.position() - 16;
864             }
865         }
866         buf.append("<<<");
867         for (int i = buffer.position(); i < buffer.limit(); i++)
868         {
869             char c = (char)buffer.get(i);
870             if (c >= ' ' && c <= 127)
871                 buf.append(c);
872             else if (c == '\r' || c == '\n')
873                 buf.append('|');
874             else
875                 buf.append('\ufffd');
876             if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32)
877             {
878                 buf.append("...");
879                 i = buffer.limit() - 16;
880             }
881         }
882         buf.append(">>>");
883         int limit = buffer.limit();
884         buffer.limit(buffer.capacity());
885         for (int i = limit; i < buffer.capacity(); i++)
886         {
887             char c = (char)buffer.get(i);
888             if (c >= ' ' && c <= 127)
889                 buf.append(c);
890             else if (c == '\r' || c == '\n')
891                 buf.append('|');
892             else
893                 buf.append('\ufffd');
894             if (i == limit + 16 && buffer.capacity() > limit + 32)
895             {
896                 buf.append("...");
897                 i = buffer.capacity() - 16;
898             }
899         }
900         buffer.limit(limit);
901         buf.append("}");
902 
903         return buf.toString();
904     }
905 
906 
907     private final static int[] decDivisors =
908             {1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
909 
910     private final static int[] hexDivisors =
911             {0x10000000, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x1};
912 
913     private final static long[] decDivisorsL =
914             {1000000000000000000L, 100000000000000000L, 10000000000000000L, 1000000000000000L, 100000000000000L, 10000000000000L, 1000000000000L, 100000000000L,
915                     10000000000L, 1000000000L, 100000000L, 10000000L, 1000000L, 100000L, 10000L, 1000L, 100L, 10L, 1L};
916 
917     public static void putCRLF(ByteBuffer buffer)
918     {
919         buffer.put((byte)13);
920         buffer.put((byte)10);
921     }
922 
923     public static boolean isPrefix(ByteBuffer prefix, ByteBuffer buffer)
924     {
925         if (prefix.remaining() > buffer.remaining())
926             return false;
927         int bi = buffer.position();
928         for (int i = prefix.position(); i < prefix.limit(); i++)
929             if (prefix.get(i) != buffer.get(bi++))
930                 return false;
931         return true;
932     }
933 
934 
935 
936 }