X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Futil%2FBufferUtil.java;fp=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Futil%2FBufferUtil.java;h=bd7cb06ecd186f0215fe65d0d6e5517400c67ebb;hp=0000000000000000000000000000000000000000;hb=73ef54a38e3930a1a789cdc6b5fa23cdd4c9d086;hpb=515007c7c1351045420669d65b59c08fa46850f2 diff --git a/lib/jetty/org/eclipse/jetty/util/BufferUtil.java b/lib/jetty/org/eclipse/jetty/util/BufferUtil.java new file mode 100644 index 00000000..bd7cb06e --- /dev/null +++ b/lib/jetty/org/eclipse/jetty/util/BufferUtil.java @@ -0,0 +1,1045 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.Buffer; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import org.eclipse.jetty.util.resource.Resource; + + +/* ------------------------------------------------------------------------------- */ +/** + * Buffer utility methods. + *

The standard JVM {@link ByteBuffer} can exist in two modes: In fill mode the valid + * data is between 0 and pos; In flush mode the valid data is between the pos and the limit. + * The various ByteBuffer methods assume a mode and some of them will switch or enforce a mode: + * Allocate and clear set fill mode; flip and compact switch modes; read and write assume fill + * and flush modes. This duality can result in confusing code such as: + *

+ *     buffer.clear();
+ *     channel.write(buffer);
+ * 
+ * Which looks as if it should write no data, but in fact writes the buffer worth of garbage. + *

+ *

+ * The BufferUtil class provides a set of utilities that operate on the convention that ByteBuffers + * will always be left, passed in an API or returned from a method in the flush mode - ie with + * valid data between the pos and limit. This convention is adopted so as to avoid confusion as to + * what state a buffer is in and to avoid excessive copying of data that can result with the usage + * of compress.

+ *

+ * Thus this class provides alternate implementations of {@link #allocate(int)}, + * {@link #allocateDirect(int)} and {@link #clear(ByteBuffer)} that leave the buffer + * in flush mode. Thus the following tests will pass:

+ *     ByteBuffer buffer = BufferUtil.allocate(1024);
+ *     assert(buffer.remaining()==0);
+ *     BufferUtil.clear(buffer);
+ *     assert(buffer.remaining()==0);
+ * 
+ *

+ *

If the BufferUtil methods {@link #fill(ByteBuffer, byte[], int, int)}, + * {@link #append(ByteBuffer, byte[], int, int)} or {@link #put(ByteBuffer, ByteBuffer)} are used, + * then the caller does not need to explicitly switch the buffer to fill mode. + * If the caller wishes to use other ByteBuffer bases libraries to fill a buffer, + * then they can use explicit calls of #flipToFill(ByteBuffer) and #flipToFlush(ByteBuffer, int) + * to change modes. Note because this convention attempts to avoid the copies of compact, the position + * is not set to zero on each fill cycle and so its value must be remembered: + *

+ *      int pos = BufferUtil.flipToFill(buffer);
+ *      try
+ *      {
+ *          buffer.put(data);
+ *      }
+ *      finally
+ *      {
+ *          flipToFlush(buffer, pos);
+ *      }
+ * 
+ * The flipToFill method will effectively clear the buffer if it is emtpy and will compact the buffer if there is no space. + * + */ +public class BufferUtil +{ + static final int TEMP_BUFFER_SIZE = 4096; + static final byte SPACE = 0x20; + static final byte MINUS = '-'; + static final byte[] DIGIT = + {(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', + (byte)'E', (byte)'F'}; + + public static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]); + + /* ------------------------------------------------------------ */ + /** Allocate ByteBuffer in flush mode. + * The position and limit will both be zero, indicating that the buffer is + * empty and must be flipped before any data is put to it. + * @param capacity capacity of the allocated ByteBuffer + * @return Buffer + */ + public static ByteBuffer allocate(int capacity) + { + ByteBuffer buf = ByteBuffer.allocate(capacity); + buf.limit(0); + return buf; + } + + /* ------------------------------------------------------------ */ + /** Allocate ByteBuffer in flush mode. + * The position and limit will both be zero, indicating that the buffer is + * empty and in flush mode. + * @param capacity capacity of the allocated ByteBuffer + * @return Buffer + */ + public static ByteBuffer allocateDirect(int capacity) + { + ByteBuffer buf = ByteBuffer.allocateDirect(capacity); + buf.limit(0); + return buf; + } + + + /* ------------------------------------------------------------ */ + /** Clear the buffer to be empty in flush mode. + * The position and limit are set to 0; + * @param buffer The buffer to clear. + */ + public static void clear(ByteBuffer buffer) + { + if (buffer != null) + { + buffer.position(0); + buffer.limit(0); + } + } + + /* ------------------------------------------------------------ */ + /** Clear the buffer to be empty in fill mode. + * The position is set to 0 and the limit is set to the capacity. + * @param buffer The buffer to clear. + */ + public static void clearToFill(ByteBuffer buffer) + { + if (buffer != null) + { + buffer.position(0); + buffer.limit(buffer.capacity()); + } + } + + /* ------------------------------------------------------------ */ + /** Flip the buffer to fill mode. + * The position is set to the first unused position in the buffer + * (the old limit) and the limit is set to the capacity. + * If the buffer is empty, then this call is effectively {@link #clearToFill(ByteBuffer)}. + * If there is no unused space to fill, a {@link ByteBuffer#compact()} is done to attempt + * to create space. + *

+ * This method is used as a replacement to {@link ByteBuffer#compact()}. + * + * @param buffer The buffer to flip + * @return The position of the valid data before the flipped position. This value should be + * passed to a subsequent call to {@link #flipToFlush(ByteBuffer, int)} + */ + public static int flipToFill(ByteBuffer buffer) + { + int position = buffer.position(); + int limit = buffer.limit(); + if (position == limit) + { + buffer.position(0); + buffer.limit(buffer.capacity()); + return 0; + } + + int capacity = buffer.capacity(); + if (limit == capacity) + { + buffer.compact(); + return 0; + } + + buffer.position(limit); + buffer.limit(capacity); + return position; + } + + + /* ------------------------------------------------------------ */ + /** Flip the buffer to Flush mode. + * The limit is set to the first unused byte(the old position) and + * the position is set to the passed position. + *

+ * This method is used as a replacement of {@link Buffer#flip()}. + * @param buffer the buffer to be flipped + * @param position The position of valid data to flip to. This should + * be the return value of the previous call to {@link #flipToFill(ByteBuffer)} + */ + public static void flipToFlush(ByteBuffer buffer, int position) + { + buffer.limit(buffer.position()); + buffer.position(position); + } + + + /* ------------------------------------------------------------ */ + /** Convert a ByteBuffer to a byte array. + * @param buffer The buffer to convert in flush mode. The buffer is not altered. + * @return An array of bytes duplicated from the buffer. + */ + public static byte[] toArray(ByteBuffer buffer) + { + if (buffer.hasArray()) + { + byte[] array = buffer.array(); + int from=buffer.arrayOffset() + buffer.position(); + return Arrays.copyOfRange(array,from,from+buffer.remaining()); + } + else + { + byte[] to = new byte[buffer.remaining()]; + buffer.slice().get(to); + return to; + } + } + + /* ------------------------------------------------------------ */ + /** Check for an empty or null buffer. + * @param buf the buffer to check + * @return true if the buffer is null or empty. + */ + public static boolean isEmpty(ByteBuffer buf) + { + return buf == null || buf.remaining() == 0; + } + + /* ------------------------------------------------------------ */ + /** Check for a non null and non empty buffer. + * @param buf the buffer to check + * @return true if the buffer is not null and not empty. + */ + public static boolean hasContent(ByteBuffer buf) + { + return buf != null && buf.remaining() > 0; + } + + /* ------------------------------------------------------------ */ + /** Check for a non null and full buffer. + * @param buf the buffer to check + * @return true if the buffer is not null and the limit equals the capacity. + */ + public static boolean isFull(ByteBuffer buf) + { + return buf != null && buf.limit() == buf.capacity(); + } + + /* ------------------------------------------------------------ */ + /** Get remaining from null checked buffer + * @param buffer The buffer to get the remaining from, in flush mode. + * @return 0 if the buffer is null, else the bytes remaining in the buffer. + */ + public static int length(ByteBuffer buffer) + { + return buffer == null ? 0 : buffer.remaining(); + } + + /* ------------------------------------------------------------ */ + /** Get the space from the limit to the capacity + * @param buffer the buffer to get the space from + * @return space + */ + public static int space(ByteBuffer buffer) + { + if (buffer == null) + return 0; + return buffer.capacity() - buffer.limit(); + } + + /* ------------------------------------------------------------ */ + /** Compact the buffer + * @param buffer the buffer to compact + * @return true if the compact made a full buffer have space + */ + public static boolean compact(ByteBuffer buffer) + { + if (buffer.position()==0) + return false; + boolean full = buffer.limit() == buffer.capacity(); + buffer.compact().flip(); + return full && buffer.limit() < buffer.capacity(); + } + + /* ------------------------------------------------------------ */ + /** + * Put data from one buffer into another, avoiding over/under flows + * @param from Buffer to take bytes from in flush mode + * @param to Buffer to put bytes to in fill mode. + * @return number of bytes moved + */ + public static int put(ByteBuffer from, ByteBuffer to) + { + int put; + int remaining = from.remaining(); + if (remaining > 0) + { + if (remaining <= to.remaining()) + { + to.put(from); + put = remaining; + from.position(0); + from.limit(0); + } + else if (from.hasArray()) + { + put = to.remaining(); + to.put(from.array(), from.arrayOffset() + from.position(), put); + from.position(from.position() + put); + } + else + { + put = to.remaining(); + ByteBuffer slice = from.slice(); + slice.limit(put); + to.put(slice); + from.position(from.position() + put); + } + } + else + put = 0; + + return put; + } + + /* ------------------------------------------------------------ */ + /** + * Put data from one buffer into another, avoiding over/under flows + * @param from Buffer to take bytes from in flush mode + * @param to Buffer to put bytes to in flush mode. The buffer is flipToFill before the put and flipToFlush after. + * @return number of bytes moved + * @deprecated use {@link #append(ByteBuffer, ByteBuffer)} + */ + public static int flipPutFlip(ByteBuffer from, ByteBuffer to) + { + return append(to,from); + } + + /* ------------------------------------------------------------ */ + /** Append bytes to a buffer. + * @param to Buffer is flush mode + * @param b bytes to append + * @param off offset into byte + * @param len length to append + * @throws BufferOverflowException + */ + public static void append(ByteBuffer to, byte[] b, int off, int len) throws BufferOverflowException + { + int pos = flipToFill(to); + try + { + to.put(b, off, len); + } + finally + { + flipToFlush(to, pos); + } + } + + /* ------------------------------------------------------------ */ + /** Appends a byte to a buffer + * @param to Buffer is flush mode + * @param b byte to append + */ + public static void append(ByteBuffer to, byte b) + { + int pos = flipToFill(to); + try + { + to.put(b); + } + finally + { + flipToFlush(to, pos); + } + } + + /* ------------------------------------------------------------ */ + /** Appends a byte to a buffer + * @param to Buffer is flush mode + * @param b bytes to append + */ + public static int append(ByteBuffer to, ByteBuffer b) + { + int pos = flipToFill(to); + try + { + return put(b, to); + } + finally + { + flipToFlush(to, pos); + } + } + + /* ------------------------------------------------------------ */ + /** + * Like append, but does not throw {@link BufferOverflowException} + * @param to Buffer is flush mode + * @param b bytes to fill + * @param off offset into byte + * @param len length to fill + */ + public static int fill(ByteBuffer to, byte[] b, int off, int len) + { + int pos = flipToFill(to); + try + { + int remaining = to.remaining(); + int take = remaining < len ? remaining : len; + to.put(b, off, take); + return take; + } + finally + { + flipToFlush(to, pos); + } + } + + + /* ------------------------------------------------------------ */ + public static void readFrom(File file, ByteBuffer buffer) throws IOException + { + try(RandomAccessFile raf = new RandomAccessFile(file,"r")) + { + FileChannel channel = raf.getChannel(); + long needed=raf.length(); + + while (needed>0 && buffer.hasRemaining()) + needed=needed-channel.read(buffer); + } + } + + /* ------------------------------------------------------------ */ + public static void readFrom(InputStream is, int needed, ByteBuffer buffer) throws IOException + { + ByteBuffer tmp = allocate(8192); + + while (needed > 0 && buffer.hasRemaining()) + { + int l = is.read(tmp.array(), 0, 8192); + if (l < 0) + break; + tmp.position(0); + tmp.limit(l); + buffer.put(tmp); + } + } + + /* ------------------------------------------------------------ */ + public static void writeTo(ByteBuffer buffer, OutputStream out) throws IOException + { + if (buffer.hasArray()) + out.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + else + { + byte[] bytes = new byte[TEMP_BUFFER_SIZE]; + while(buffer.hasRemaining()){ + int byteCountToWrite = Math.min(buffer.remaining(), TEMP_BUFFER_SIZE); + buffer.get(bytes, 0, byteCountToWrite); + out.write(bytes,0 , byteCountToWrite); + } + } + } + + /* ------------------------------------------------------------ */ + /** Convert the buffer to an ISO-8859-1 String + * @param buffer The buffer to convert in flush mode. The buffer is unchanged + * @return The buffer as a string. + */ + public static String toString(ByteBuffer buffer) + { + return toString(buffer, StandardCharsets.ISO_8859_1); + } + + /* ------------------------------------------------------------ */ + /** Convert the buffer to an UTF-8 String + * @param buffer The buffer to convert in flush mode. The buffer is unchanged + * @return The buffer as a string. + */ + public static String toUTF8String(ByteBuffer buffer) + { + return toString(buffer, StandardCharsets.UTF_8); + } + + /* ------------------------------------------------------------ */ + /** Convert the buffer to an ISO-8859-1 String + * @param buffer The buffer to convert in flush mode. The buffer is unchanged + * @param charset The {@link Charset} to use to convert the bytes + * @return The buffer as a string. + */ + public static String toString(ByteBuffer buffer, Charset charset) + { + if (buffer == null) + return null; + byte[] array = buffer.hasArray() ? buffer.array() : null; + if (array == null) + { + byte[] to = new byte[buffer.remaining()]; + buffer.slice().get(to); + return new String(to, 0, to.length, charset); + } + return new String(array, buffer.arrayOffset() + buffer.position(), buffer.remaining(), charset); + } + + /* ------------------------------------------------------------ */ + /** Convert a partial buffer to an ISO-8859-1 String + * @param buffer The buffer to convert in flush mode. The buffer is unchanged + * @param charset The {@link Charset} to use to convert the bytes + * @return The buffer as a string. + */ + public static String toString(ByteBuffer buffer, int position, int length, Charset charset) + { + if (buffer == null) + return null; + byte[] array = buffer.hasArray() ? buffer.array() : null; + if (array == null) + { + ByteBuffer ro = buffer.asReadOnlyBuffer(); + ro.position(position); + ro.limit(position + length); + byte[] to = new byte[length]; + ro.get(to); + return new String(to, 0, to.length, charset); + } + return new String(array, buffer.arrayOffset() + position, length, charset); + } + + /* ------------------------------------------------------------ */ + /** + * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown + * + * @param buffer + * A buffer containing an integer in flush mode. The position is not changed. + * @return an int + */ + public static int toInt(ByteBuffer buffer) + { + int val = 0; + boolean started = false; + boolean minus = false; + + for (int i = buffer.position(); i < buffer.limit(); i++) + { + byte b = buffer.get(i); + if (b <= SPACE) + { + if (started) + break; + } + else if (b >= '0' && b <= '9') + { + val = val * 10 + (b - '0'); + started = true; + } + else if (b == MINUS && !started) + { + minus = true; + } + else + break; + } + + if (started) + return minus ? (-val) : val; + throw new NumberFormatException(toString(buffer)); + } + + /* ------------------------------------------------------------ */ + /** + * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown + * + * @param buffer + * A buffer containing an integer in flush mode. The position is updated. + * @return an int + */ + public static int takeInt(ByteBuffer buffer) + { + int val = 0; + boolean started = false; + boolean minus = false; + int i; + for (i = buffer.position(); i < buffer.limit(); i++) + { + byte b = buffer.get(i); + if (b <= SPACE) + { + if (started) + break; + } + else if (b >= '0' && b <= '9') + { + val = val * 10 + (b - '0'); + started = true; + } + else if (b == MINUS && !started) + { + minus = true; + } + else + break; + } + + if (started) + { + buffer.position(i); + return minus ? (-val) : val; + } + throw new NumberFormatException(toString(buffer)); + } + + /** + * Convert buffer to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown + * + * @param buffer + * A buffer containing an integer in flush mode. The position is not changed. + * @return an int + */ + public static long toLong(ByteBuffer buffer) + { + long val = 0; + boolean started = false; + boolean minus = false; + + for (int i = buffer.position(); i < buffer.limit(); i++) + { + byte b = buffer.get(i); + if (b <= SPACE) + { + if (started) + break; + } + else if (b >= '0' && b <= '9') + { + val = val * 10L + (b - '0'); + started = true; + } + else if (b == MINUS && !started) + { + minus = true; + } + else + break; + } + + if (started) + return minus ? (-val) : val; + throw new NumberFormatException(toString(buffer)); + } + + public static void putHexInt(ByteBuffer buffer, int n) + { + if (n < 0) + { + buffer.put((byte)'-'); + + if (n == Integer.MIN_VALUE) + { + buffer.put((byte)(0x7f & '8')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + buffer.put((byte)(0x7f & '0')); + + return; + } + n = -n; + } + + if (n < 0x10) + { + buffer.put(DIGIT[n]); + } + else + { + boolean started = false; + // This assumes constant time int arithmatic + for (int hexDivisor : hexDivisors) + { + if (n < hexDivisor) + { + if (started) + buffer.put((byte)'0'); + continue; + } + + started = true; + int d = n / hexDivisor; + buffer.put(DIGIT[d]); + n = n - d * hexDivisor; + } + } + } + + /* ------------------------------------------------------------ */ + public static void putDecInt(ByteBuffer buffer, int n) + { + if (n < 0) + { + buffer.put((byte)'-'); + + if (n == Integer.MIN_VALUE) + { + buffer.put((byte)'2'); + n = 147483648; + } + else + n = -n; + } + + if (n < 10) + { + buffer.put(DIGIT[n]); + } + else + { + boolean started = false; + // This assumes constant time int arithmatic + for (int decDivisor : decDivisors) + { + if (n < decDivisor) + { + if (started) + buffer.put((byte)'0'); + continue; + } + + started = true; + int d = n / decDivisor; + buffer.put(DIGIT[d]); + n = n - d * decDivisor; + } + } + } + + public static void putDecLong(ByteBuffer buffer, long n) + { + if (n < 0) + { + buffer.put((byte)'-'); + + if (n == Long.MIN_VALUE) + { + buffer.put((byte)'9'); + n = 223372036854775808L; + } + else + n = -n; + } + + if (n < 10) + { + buffer.put(DIGIT[(int)n]); + } + else + { + boolean started = false; + // This assumes constant time int arithmatic + for (long aDecDivisorsL : decDivisorsL) + { + if (n < aDecDivisorsL) + { + if (started) + buffer.put((byte)'0'); + continue; + } + + started = true; + long d = n / aDecDivisorsL; + buffer.put(DIGIT[(int)d]); + n = n - d * aDecDivisorsL; + } + } + } + + public static ByteBuffer toBuffer(int value) + { + ByteBuffer buf = ByteBuffer.allocate(32); + putDecInt(buf, value); + return buf; + } + + public static ByteBuffer toBuffer(long value) + { + ByteBuffer buf = ByteBuffer.allocate(32); + putDecLong(buf, value); + return buf; + } + + public static ByteBuffer toBuffer(String s) + { + return ByteBuffer.wrap(s.getBytes(StandardCharsets.ISO_8859_1)); + } + + public static ByteBuffer toDirectBuffer(String s) + { + byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1); + ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length); + buf.put(bytes); + buf.flip(); + return buf; + } + + public static ByteBuffer toBuffer(String s, Charset charset) + { + return ByteBuffer.wrap(s.getBytes(charset)); + } + + public static ByteBuffer toDirectBuffer(String s, Charset charset) + { + byte[] bytes = s.getBytes(charset); + ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length); + buf.put(bytes); + buf.flip(); + return buf; + } + + /** + * Create a new ByteBuffer using provided byte array. + * + * @param array + * the byte array to back buffer with. + * @return ByteBuffer with provided byte array, in flush mode + */ + public static ByteBuffer toBuffer(byte array[]) + { + return ByteBuffer.wrap(array); + } + + /** + * Create a new ByteBuffer using the provided byte array. + * + * @param array + * the byte array to use. + * @param offset + * the offset within the byte array to use from + * @param length + * the length in bytes of the array to use + * @return ByteBuffer with provided byte array, in flush mode + */ + public static ByteBuffer toBuffer(byte array[], int offset, int length) + { + return ByteBuffer.wrap(array, offset, length); + } + + public static ByteBuffer toMappedBuffer(File file) throws IOException + { + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) + { + return raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()); + } + } + + public static ByteBuffer toBuffer(Resource resource,boolean direct) throws IOException + { + int len=(int)resource.length(); + if (len<0) + throw new IllegalArgumentException("invalid resource: "+String.valueOf(resource)+" len="+len); + + ByteBuffer buffer = direct?BufferUtil.allocateDirect(len):BufferUtil.allocate(len); + + int pos=BufferUtil.flipToFill(buffer); + if (resource.getFile()!=null) + BufferUtil.readFrom(resource.getFile(),buffer); + else + { + try (InputStream is = resource.getInputStream();) + { + BufferUtil.readFrom(is,len,buffer); + } + } + BufferUtil.flipToFlush(buffer,pos); + + return buffer; + } + + public static String toSummaryString(ByteBuffer buffer) + { + if (buffer == null) + return "null"; + StringBuilder buf = new StringBuilder(); + buf.append("[p="); + buf.append(buffer.position()); + buf.append(",l="); + buf.append(buffer.limit()); + buf.append(",c="); + buf.append(buffer.capacity()); + buf.append(",r="); + buf.append(buffer.remaining()); + buf.append("]"); + return buf.toString(); + } + + public static String toDetailString(ByteBuffer[] buffer) + { + StringBuilder builder = new StringBuilder(); + builder.append('['); + for (int i = 0; i < buffer.length; i++) + { + if (i > 0) builder.append(','); + builder.append(toDetailString(buffer[i])); + } + builder.append(']'); + return builder.toString(); + } + + public static String toDetailString(ByteBuffer buffer) + { + if (buffer == null) + return "null"; + + StringBuilder buf = new StringBuilder(); + buf.append(buffer.getClass().getSimpleName()); + buf.append("@"); + if (buffer.hasArray()) + buf.append(Integer.toHexString(((Object)buffer.array()).hashCode())); + else + buf.append(Integer.toHexString(buf.hashCode())); + buf.append("[p="); + buf.append(buffer.position()); + buf.append(",l="); + buf.append(buffer.limit()); + buf.append(",c="); + buf.append(buffer.capacity()); + buf.append(",r="); + buf.append(buffer.remaining()); + buf.append("]={"); + + for (int i = 0; i < buffer.position(); i++) + { + char c = (char)buffer.get(i); + if (c >= ' ' && c <= 127) + buf.append(c); + else if (c == '\r' || c == '\n') + buf.append('|'); + else + buf.append('\ufffd'); + if (i == 16 && buffer.position() > 32) + { + buf.append("..."); + i = buffer.position() - 16; + } + } + buf.append("<<<"); + for (int i = buffer.position(); i < buffer.limit(); i++) + { + char c = (char)buffer.get(i); + if (c >= ' ' && c <= 127) + buf.append(c); + else if (c == '\r' || c == '\n') + buf.append('|'); + else + buf.append('\ufffd'); + if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32) + { + buf.append("..."); + i = buffer.limit() - 16; + } + } + buf.append(">>>"); + int limit = buffer.limit(); + buffer.limit(buffer.capacity()); + for (int i = limit; i < buffer.capacity(); i++) + { + char c = (char)buffer.get(i); + if (c >= ' ' && c <= 127) + buf.append(c); + else if (c == '\r' || c == '\n') + buf.append('|'); + else + buf.append('\ufffd'); + if (i == limit + 16 && buffer.capacity() > limit + 32) + { + buf.append("..."); + i = buffer.capacity() - 16; + } + } + buffer.limit(limit); + buf.append("}"); + + return buf.toString(); + } + + + private final static int[] decDivisors = + {1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1}; + + private final static int[] hexDivisors = + {0x10000000, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x1}; + + private final static long[] decDivisorsL = + {1000000000000000000L, 100000000000000000L, 10000000000000000L, 1000000000000000L, 100000000000000L, 10000000000000L, 1000000000000L, 100000000000L, + 10000000000L, 1000000000L, 100000000L, 10000000L, 1000000L, 100000L, 10000L, 1000L, 100L, 10L, 1L}; + + public static void putCRLF(ByteBuffer buffer) + { + buffer.put((byte)13); + buffer.put((byte)10); + } + + public static boolean isPrefix(ByteBuffer prefix, ByteBuffer buffer) + { + if (prefix.remaining() > buffer.remaining()) + return false; + int bi = buffer.position(); + for (int i = prefix.position(); i < prefix.limit(); i++) + if (prefix.get(i) != buffer.get(bi++)) + return false; + return true; + } + + public static ByteBuffer ensureCapacity(ByteBuffer buffer, int capacity) + { + if (buffer==null) + return allocate(capacity); + + if (buffer.capacity()>=capacity) + return buffer; + + if (buffer.hasArray()) + return ByteBuffer.wrap(Arrays.copyOfRange(buffer.array(), buffer.arrayOffset(), buffer.arrayOffset()+capacity),buffer.position(),buffer.remaining()); + + throw new UnsupportedOperationException(); + } + + + +}