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