| // |
| // ======================================================================== |
| // 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.io; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.charset.Charset; |
| |
| import org.eclipse.jetty.util.TypeUtil; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| |
| /** |
| * |
| * |
| */ |
| public abstract class AbstractBuffer implements Buffer |
| { |
| private static final Logger LOG = Log.getLogger(AbstractBuffer.class); |
| |
| private final static boolean __boundsChecking = Boolean.getBoolean("org.eclipse.jetty.io.AbstractBuffer.boundsChecking"); |
| |
| protected final static String |
| __IMMUTABLE = "IMMUTABLE", |
| __READONLY = "READONLY", |
| __READWRITE = "READWRITE", |
| __VOLATILE = "VOLATILE"; |
| |
| protected int _access; |
| protected boolean _volatile; |
| |
| protected int _get; |
| protected int _put; |
| protected int _hash; |
| protected int _hashGet; |
| protected int _hashPut; |
| protected int _mark; |
| protected String _string; |
| protected View _view; |
| |
| /** |
| * Constructor for BufferView |
| * |
| * @param access 0==IMMUTABLE, 1==READONLY, 2==READWRITE |
| */ |
| public AbstractBuffer(int access, boolean isVolatile) |
| { |
| if (access == IMMUTABLE && isVolatile) |
| throw new IllegalArgumentException("IMMUTABLE && VOLATILE"); |
| setMarkIndex(-1); |
| _access = access; |
| _volatile = isVolatile; |
| } |
| |
| /* |
| * @see org.eclipse.io.Buffer#toArray() |
| */ |
| public byte[] asArray() |
| { |
| byte[] bytes = new byte[length()]; |
| byte[] array = array(); |
| if (array != null) |
| System.arraycopy(array, getIndex(), bytes, 0, bytes.length); |
| else |
| peek(getIndex(), bytes, 0, length()); |
| return bytes; |
| } |
| |
| public ByteArrayBuffer duplicate(int access) |
| { |
| Buffer b=this.buffer(); |
| if (this instanceof Buffer.CaseInsensitve || b instanceof Buffer.CaseInsensitve) |
| return new ByteArrayBuffer.CaseInsensitive(asArray(), 0, length(),access); |
| else |
| return new ByteArrayBuffer(asArray(), 0, length(), access); |
| } |
| |
| /* |
| * @see org.eclipse.io.Buffer#asNonVolatile() |
| */ |
| public Buffer asNonVolatileBuffer() |
| { |
| if (!isVolatile()) return this; |
| return duplicate(_access); |
| } |
| |
| public Buffer asImmutableBuffer() |
| { |
| if (isImmutable()) return this; |
| return duplicate(IMMUTABLE); |
| } |
| |
| /* |
| * @see org.eclipse.util.Buffer#asReadOnlyBuffer() |
| */ |
| public Buffer asReadOnlyBuffer() |
| { |
| if (isReadOnly()) return this; |
| return new View(this, markIndex(), getIndex(), putIndex(), READONLY); |
| } |
| |
| public Buffer asMutableBuffer() |
| { |
| if (!isImmutable()) return this; |
| |
| Buffer b=this.buffer(); |
| if (b.isReadOnly()) |
| { |
| return duplicate(READWRITE); |
| } |
| return new View(b, markIndex(), getIndex(), putIndex(), _access); |
| } |
| |
| public Buffer buffer() |
| { |
| return this; |
| } |
| |
| public void clear() |
| { |
| setMarkIndex(-1); |
| setGetIndex(0); |
| setPutIndex(0); |
| } |
| |
| public void compact() |
| { |
| if (isReadOnly()) throw new IllegalStateException(__READONLY); |
| int s = markIndex() >= 0 ? markIndex() : getIndex(); |
| if (s > 0) |
| { |
| byte array[] = array(); |
| int length = putIndex() - s; |
| if (length > 0) |
| { |
| if (array != null) |
| System.arraycopy(array(), s, array(), 0, length); |
| else |
| poke(0, peek(s, length)); |
| } |
| if (markIndex() > 0) setMarkIndex(markIndex() - s); |
| setGetIndex(getIndex() - s); |
| setPutIndex(putIndex() - s); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| if (obj==this) |
| return true; |
| |
| // reject non buffers; |
| if (obj == null || !(obj instanceof Buffer)) return false; |
| Buffer b = (Buffer) obj; |
| |
| if (this instanceof Buffer.CaseInsensitve || b instanceof Buffer.CaseInsensitve) |
| return equalsIgnoreCase(b); |
| |
| // reject different lengths |
| if (b.length() != length()) return false; |
| |
| // reject AbstractBuffer with different hash value |
| if (_hash != 0 && obj instanceof AbstractBuffer) |
| { |
| AbstractBuffer ab = (AbstractBuffer) obj; |
| if (ab._hash != 0 && _hash != ab._hash) return false; |
| } |
| |
| // Nothing for it but to do the hard grind. |
| int get=getIndex(); |
| int bi=b.putIndex(); |
| for (int i = putIndex(); i-->get;) |
| { |
| byte b1 = peek(i); |
| byte b2 = b.peek(--bi); |
| if (b1 != b2) return false; |
| } |
| return true; |
| } |
| |
| public boolean equalsIgnoreCase(Buffer b) |
| { |
| if (b==this) |
| return true; |
| |
| // reject different lengths |
| if (b.length() != length()) return false; |
| |
| // reject AbstractBuffer with different hash value |
| if (_hash != 0 && b instanceof AbstractBuffer) |
| { |
| AbstractBuffer ab = (AbstractBuffer) b; |
| if (ab._hash != 0 && _hash != ab._hash) return false; |
| } |
| |
| // Nothing for it but to do the hard grind. |
| int get=getIndex(); |
| int bi=b.putIndex(); |
| |
| byte[] array = array(); |
| byte[] barray= b.array(); |
| if (array!=null && barray!=null) |
| { |
| for (int i = putIndex(); i-->get;) |
| { |
| byte b1 = array[i]; |
| byte b2 = barray[--bi]; |
| if (b1 != b2) |
| { |
| if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A'); |
| if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A'); |
| if (b1 != b2) return false; |
| } |
| } |
| } |
| else |
| { |
| for (int i = putIndex(); i-->get;) |
| { |
| byte b1 = peek(i); |
| byte b2 = b.peek(--bi); |
| if (b1 != b2) |
| { |
| if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A'); |
| if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A'); |
| if (b1 != b2) return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| public byte get() |
| { |
| return peek(_get++); |
| } |
| |
| public int get(byte[] b, int offset, int length) |
| { |
| int gi = getIndex(); |
| int l=length(); |
| if (l==0) |
| return -1; |
| |
| if (length>l) |
| length=l; |
| |
| length = peek(gi, b, offset, length); |
| if (length>0) |
| setGetIndex(gi + length); |
| return length; |
| } |
| |
| public Buffer get(int length) |
| { |
| int gi = getIndex(); |
| Buffer view = peek(gi, length); |
| setGetIndex(gi + length); |
| return view; |
| } |
| |
| public final int getIndex() |
| { |
| return _get; |
| } |
| |
| public boolean hasContent() |
| { |
| return _put > _get; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) |
| { |
| int get=getIndex(); |
| byte[] array = array(); |
| if (array==null) |
| { |
| for (int i = putIndex(); i-- >get;) |
| { |
| byte b = peek(i); |
| if ('a' <= b && b <= 'z') |
| b = (byte) (b - 'a' + 'A'); |
| _hash = 31 * _hash + b; |
| } |
| } |
| else |
| { |
| for (int i = putIndex(); i-- >get;) |
| { |
| byte b = array[i]; |
| if ('a' <= b && b <= 'z') |
| b = (byte) (b - 'a' + 'A'); |
| _hash = 31 * _hash + b; |
| } |
| } |
| if (_hash == 0) |
| _hash = -1; |
| _hashGet=_get; |
| _hashPut=_put; |
| |
| } |
| return _hash; |
| } |
| |
| public boolean isImmutable() |
| { |
| return _access <= IMMUTABLE; |
| } |
| |
| public boolean isReadOnly() |
| { |
| return _access <= READONLY; |
| } |
| |
| public boolean isVolatile() |
| { |
| return _volatile; |
| } |
| |
| public int length() |
| { |
| return _put - _get; |
| } |
| |
| public void mark() |
| { |
| setMarkIndex(_get - 1); |
| } |
| |
| public void mark(int offset) |
| { |
| setMarkIndex(_get + offset); |
| } |
| |
| public int markIndex() |
| { |
| return _mark; |
| } |
| |
| public byte peek() |
| { |
| return peek(_get); |
| } |
| |
| public Buffer peek(int index, int length) |
| { |
| if (_view == null) |
| { |
| _view = new View(this, -1, index, index + length, isReadOnly() ? READONLY : READWRITE); |
| } |
| else |
| { |
| _view.update(this.buffer()); |
| _view.setMarkIndex(-1); |
| _view.setGetIndex(0); |
| _view.setPutIndex(index + length); |
| _view.setGetIndex(index); |
| |
| } |
| return _view; |
| } |
| |
| public int poke(int index, Buffer src) |
| { |
| _hash=0; |
| /* |
| if (isReadOnly()) |
| throw new IllegalStateException(__READONLY); |
| if (index < 0) |
| throw new IllegalArgumentException("index<0: " + index + "<0"); |
| */ |
| |
| int length=src.length(); |
| if (index + length > capacity()) |
| { |
| length=capacity()-index; |
| /* |
| if (length<0) |
| throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity()); |
| */ |
| } |
| |
| byte[] src_array = src.array(); |
| byte[] dst_array = array(); |
| if (src_array != null && dst_array != null) |
| System.arraycopy(src_array, src.getIndex(), dst_array, index, length); |
| else if (src_array != null) |
| { |
| int s=src.getIndex(); |
| for (int i=0;i<length;i++) |
| poke(index++,src_array[s++]); |
| } |
| else if (dst_array != null) |
| { |
| int s=src.getIndex(); |
| for (int i=0;i<length;i++) |
| dst_array[index++]=src.peek(s++); |
| } |
| else |
| { |
| int s=src.getIndex(); |
| for (int i=0;i<length;i++) |
| poke(index++,src.peek(s++)); |
| } |
| |
| return length; |
| } |
| |
| |
| public int poke(int index, byte[] b, int offset, int length) |
| { |
| _hash=0; |
| /* |
| if (isReadOnly()) |
| throw new IllegalStateException(__READONLY); |
| if (index < 0) |
| throw new IllegalArgumentException("index<0: " + index + "<0"); |
| */ |
| if (index + length > capacity()) |
| { |
| length=capacity()-index; |
| /* if (length<0) |
| throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity()); |
| */ |
| } |
| |
| byte[] dst_array = array(); |
| if (dst_array != null) |
| System.arraycopy(b, offset, dst_array, index, length); |
| else |
| { |
| int s=offset; |
| for (int i=0;i<length;i++) |
| poke(index++,b[s++]); |
| } |
| return length; |
| } |
| |
| public int put(Buffer src) |
| { |
| int pi = putIndex(); |
| int l=poke(pi, src); |
| setPutIndex(pi + l); |
| return l; |
| } |
| |
| public void put(byte b) |
| { |
| int pi = putIndex(); |
| poke(pi, b); |
| setPutIndex(pi + 1); |
| } |
| |
| public int put(byte[] b, int offset, int length) |
| { |
| int pi = putIndex(); |
| int l = poke(pi, b, offset, length); |
| setPutIndex(pi + l); |
| return l; |
| } |
| |
| public int put(byte[] b) |
| { |
| int pi = putIndex(); |
| int l = poke(pi, b, 0, b.length); |
| setPutIndex(pi + l); |
| return l; |
| } |
| |
| public final int putIndex() |
| { |
| return _put; |
| } |
| |
| public void reset() |
| { |
| if (markIndex() >= 0) setGetIndex(markIndex()); |
| } |
| |
| public void rewind() |
| { |
| setGetIndex(0); |
| setMarkIndex(-1); |
| } |
| |
| public void setGetIndex(int getIndex) |
| { |
| /* bounds checking |
| if (isImmutable()) |
| throw new IllegalStateException(__IMMUTABLE); |
| if (getIndex < 0) |
| throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0"); |
| if (getIndex > putIndex()) |
| throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex()); |
| */ |
| _get = getIndex; |
| _hash=0; |
| } |
| |
| public void setMarkIndex(int index) |
| { |
| /* |
| if (index>=0 && isImmutable()) |
| throw new IllegalStateException(__IMMUTABLE); |
| */ |
| _mark = index; |
| } |
| |
| public void setPutIndex(int putIndex) |
| { |
| /* bounds checking |
| if (isImmutable()) |
| throw new IllegalStateException(__IMMUTABLE); |
| if (putIndex > capacity()) |
| throw new IllegalArgumentException("putIndex>capacity: " + putIndex + ">" + capacity()); |
| if (getIndex() > putIndex) |
| throw new IllegalArgumentException("getIndex>putIndex: " + getIndex() + ">" + putIndex); |
| */ |
| _put = putIndex; |
| _hash=0; |
| } |
| |
| public int skip(int n) |
| { |
| if (length() < n) n = length(); |
| setGetIndex(getIndex() + n); |
| return n; |
| } |
| |
| public Buffer slice() |
| { |
| return peek(getIndex(), length()); |
| } |
| |
| public Buffer sliceFromMark() |
| { |
| return sliceFromMark(getIndex() - markIndex() - 1); |
| } |
| |
| public Buffer sliceFromMark(int length) |
| { |
| if (markIndex() < 0) return null; |
| Buffer view = peek(markIndex(), length); |
| setMarkIndex(-1); |
| return view; |
| } |
| |
| public int space() |
| { |
| return capacity() - _put; |
| } |
| |
| public String toDetailString() |
| { |
| StringBuilder buf = new StringBuilder(); |
| buf.append("["); |
| buf.append(super.hashCode()); |
| buf.append(","); |
| buf.append(this.buffer().hashCode()); |
| buf.append(",m="); |
| buf.append(markIndex()); |
| buf.append(",g="); |
| buf.append(getIndex()); |
| buf.append(",p="); |
| buf.append(putIndex()); |
| buf.append(",c="); |
| buf.append(capacity()); |
| buf.append("]={"); |
| if (markIndex() >= 0) |
| { |
| for (int i = markIndex(); i < getIndex(); i++) |
| { |
| byte b = peek(i); |
| TypeUtil.toHex(b,buf); |
| } |
| buf.append("}{"); |
| } |
| int count = 0; |
| for (int i = getIndex(); i < putIndex(); i++) |
| { |
| byte b = peek(i); |
| TypeUtil.toHex(b,buf); |
| if (count++ == 50) |
| { |
| if (putIndex() - i > 20) |
| { |
| buf.append(" ... "); |
| i = putIndex() - 20; |
| } |
| } |
| } |
| buf.append('}'); |
| return buf.toString(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public String toString() |
| { |
| if (isImmutable()) |
| { |
| if (_string == null) |
| _string = new String(asArray(), 0, length()); |
| return _string; |
| } |
| return new String(asArray(), 0, length()); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public String toString(String charset) |
| { |
| try |
| { |
| byte[] bytes=array(); |
| if (bytes!=null) |
| return new String(bytes,getIndex(),length(),charset); |
| return new String(asArray(), 0, length(),charset); |
| |
| } |
| catch(Exception e) |
| { |
| LOG.warn(e); |
| return new String(asArray(), 0, length()); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public String toString(Charset charset) |
| { |
| try |
| { |
| byte[] bytes=array(); |
| if (bytes!=null) |
| return new String(bytes,getIndex(),length(),charset); |
| return new String(asArray(), 0, length(),charset); |
| } |
| catch(Exception e) |
| { |
| LOG.warn(e); |
| return new String(asArray(), 0, length()); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public String toDebugString() |
| { |
| return getClass()+"@"+super.hashCode(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void writeTo(OutputStream out) |
| throws IOException |
| { |
| byte[] array = array(); |
| |
| if (array!=null) |
| { |
| out.write(array,getIndex(),length()); |
| } |
| else |
| { |
| int len = this.length(); |
| byte[] buf=new byte[len>1024?1024:len]; |
| int offset=_get; |
| while (len>0) |
| { |
| int l=peek(offset,buf,0,len>buf.length?buf.length:len); |
| out.write(buf,0,l); |
| offset+=l; |
| len-=l; |
| } |
| } |
| clear(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public int readFrom(InputStream in,int max) throws IOException |
| { |
| byte[] array = array(); |
| int s=space(); |
| if (s>max) |
| s=max; |
| |
| if (array!=null) |
| { |
| int l=in.read(array,_put,s); |
| if (l>0) |
| _put+=l; |
| return l; |
| } |
| else |
| { |
| byte[] buf=new byte[s>1024?1024:s]; |
| int total=0; |
| while (s>0) |
| { |
| int l=in.read(buf,0,buf.length); |
| if (l<0) |
| return total>0?total:-1; |
| int p=put(buf,0,l); |
| assert l==p; |
| s-=l; |
| } |
| return total; |
| } |
| } |
| } |