All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.util.BufferUtil Maven / Gradle / Ivy

There is a newer version: 12.0.13
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2013 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.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;


/* ------------------------------------------------------------------------------- */
/**
 * Buffer utility methods.
 *
 * These utility methods facilitate the usage of NIO {@link ByteBuffer}'s in a more flexible way.
 * The standard {@link ByteBuffer#flip()} assumes that once flipped to flush a buffer,
 * that it will be completely emptied before being cleared ready to be filled again.
 * The {@link #flipToFill(ByteBuffer)} and {@link #flipToFlush(ByteBuffer, int)} methods provided here
 * do not assume that the buffer is empty and will preserve content when flipped.
 * 

* ByteBuffers can be considered in one of two modes: Flush mode where valid content is contained between * position and limit which is consumed by advancing the position; and Fill mode where empty space is between * the position and limit, which is filled by advancing the position. In fill mode, there may be valid data * in the buffer before the position and the start of this data is given by the return value of {@link #flipToFill(ByteBuffer)} *

* A typical pattern for using the buffers in this style is: *

 *    ByteBuffer buf = BufferUtil.allocate(4096);
 *    while(in.isOpen())
 *    {
 *        int pos=BufferUtil.flipToFill(buf);
 *        if (in.read(buf)<0)
 *          break;
 *        BufferUtil.flipToFlush(buf,pos);
 *        out.write(buf);
 *    }
*/ public class BufferUtil { 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 * @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 must be flipped before any data is put to it. * @param capacity * @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) { byte[] to = new byte[buffer.remaining()]; if (buffer.hasArray()) { byte[] array = buffer.array(); System.arraycopy(array,buffer.arrayOffset()+buffer.position(),to,0,to.length); } else 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 * @return space */ public static int space(ByteBuffer buffer) { if (buffer==null) return 0; return buffer.capacity()-buffer.limit(); } /* ------------------------------------------------------------ */ /** Compact the buffer * @param buffer * @return true if the compact made a full buffer have space */ public static boolean compact(ByteBuffer buffer) { boolean full=buffer.limit()==buffer.capacity(); buffer.compact().flip(); return full && buffer.limit()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 */ public static int flipPutFlip(ByteBuffer from, ByteBuffer to) { int pos= flipToFill(to); try { return put(from,to); } finally { flipToFlush(to,pos); } } /* ------------------------------------------------------------ */ /** */ public static void append(ByteBuffer to, byte[] b,int off,int len) { int pos= flipToFill(to); try { to.put(b,off,len); } finally { flipToFlush(to,pos); } } /* ------------------------------------------------------------ */ /** */ public static void append(ByteBuffer to, byte b) { int limit=to.limit(); to.limit(limit+1); to.put(limit,b); } /* ------------------------------------------------------------ */ public static void readFrom(File file, ByteBuffer buffer) throws IOException { 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 { // TODO this is horribly inefficient for (int i=buffer.position();i= '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 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 i = 0; i < hexDivisors.length; i++) { if (n < hexDivisors[i]) { if (started) buffer.put((byte)'0'); continue; } started = true; int d = n / hexDivisors[i]; buffer.put(DIGIT[d]); n = n - d * hexDivisors[i]; } } } /* ------------------------------------------------------------ */ 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 i = 0; i < decDivisors.length; i++) { if (n < decDivisors[i]) { if (started) buffer.put((byte)'0'); continue; } started = true; int d = n / decDivisors[i]; buffer.put(DIGIT[d]); n = n - d * decDivisors[i]; } } } 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 (int i = 0; i < decDivisorsL.length; i++) { if (n < decDivisorsL[i]) { if (started) buffer.put((byte)'0'); continue; } started = true; long d = n / decDivisorsL[i]; buffer.put(DIGIT[(int)d]); n = n - d * decDivisorsL[i]; } } } 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(StringUtil.__ISO_8859_1_CHARSET)); } public static ByteBuffer toDirectBuffer(String s) { byte[] bytes=s.getBytes(StringUtil.__ISO_8859_1_CHARSET); 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 toBuffer(File file) throws IOException { RandomAccessFile raf = new RandomAccessFile(file,"r"); MappedByteBuffer buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length()); 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;i0) 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=' ' && 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=' ' && 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=' ' && 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy