| // ======================================================================== |
| // Copyright (c) 2004-2009 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.io.UnsupportedEncodingException; |
| |
| import org.eclipse.jetty.util.StringUtil; |
| |
| /* ------------------------------------------------------------------------------- */ |
| /** |
| * |
| */ |
| public class ByteArrayBuffer extends AbstractBuffer |
| { |
| final protected byte[] _bytes; |
| |
| protected ByteArrayBuffer(int size, int access, boolean isVolatile) |
| { |
| this(new byte[size],0,0,access, isVolatile); |
| } |
| |
| public ByteArrayBuffer(byte[] bytes) |
| { |
| this(bytes, 0, bytes.length, READWRITE); |
| } |
| |
| public ByteArrayBuffer(byte[] bytes, int index, int length) |
| { |
| this(bytes, index, length, READWRITE); |
| } |
| |
| public ByteArrayBuffer(byte[] bytes, int index, int length, int access) |
| { |
| super(READWRITE, NON_VOLATILE); |
| _bytes = bytes; |
| setPutIndex(index + length); |
| setGetIndex(index); |
| _access = access; |
| } |
| |
| public ByteArrayBuffer(byte[] bytes, int index, int length, int access, boolean isVolatile) |
| { |
| super(READWRITE, isVolatile); |
| _bytes = bytes; |
| setPutIndex(index + length); |
| setGetIndex(index); |
| _access = access; |
| } |
| |
| public ByteArrayBuffer(int size) |
| { |
| this(new byte[size], 0, 0, READWRITE); |
| setPutIndex(0); |
| } |
| |
| public ByteArrayBuffer(String value) |
| { |
| super(READWRITE,NON_VOLATILE); |
| _bytes = StringUtil.getBytes(value); |
| setGetIndex(0); |
| setPutIndex(_bytes.length); |
| _access=IMMUTABLE; |
| _string = value; |
| } |
| |
| public ByteArrayBuffer(String value,boolean immutable) |
| { |
| super(READWRITE,NON_VOLATILE); |
| _bytes = StringUtil.getBytes(value); |
| setGetIndex(0); |
| setPutIndex(_bytes.length); |
| if (immutable) |
| { |
| _access=IMMUTABLE; |
| _string = value; |
| } |
| } |
| |
| public ByteArrayBuffer(String value,String encoding) throws UnsupportedEncodingException |
| { |
| super(READWRITE,NON_VOLATILE); |
| _bytes = value.getBytes(encoding); |
| setGetIndex(0); |
| setPutIndex(_bytes.length); |
| _access=IMMUTABLE; |
| _string = value; |
| } |
| |
| public byte[] array() |
| { |
| return _bytes; |
| } |
| |
| public int capacity() |
| { |
| return _bytes.length; |
| } |
| |
| @Override |
| public void compact() |
| { |
| if (isReadOnly()) |
| throw new IllegalStateException(__READONLY); |
| int s = markIndex() >= 0 ? markIndex() : getIndex(); |
| if (s > 0) |
| { |
| int length = putIndex() - s; |
| if (length > 0) |
| { |
| System.arraycopy(_bytes, s,_bytes, 0, length); |
| } |
| if (markIndex() > 0) setMarkIndex(markIndex() - s); |
| setGetIndex(getIndex() - s); |
| setPutIndex(putIndex() - s); |
| } |
| } |
| |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| if (obj==this) |
| return true; |
| |
| if (obj == null || !(obj instanceof Buffer)) |
| return false; |
| |
| if (obj instanceof Buffer.CaseInsensitve) |
| return equalsIgnoreCase((Buffer)obj); |
| |
| |
| Buffer b = (Buffer) obj; |
| |
| // 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 = _bytes[i]; |
| byte b2 = b.peek(--bi); |
| if (b1 != b2) return false; |
| } |
| return true; |
| } |
| |
| |
| @Override |
| public boolean equalsIgnoreCase(Buffer b) |
| { |
| if (b==this) |
| return true; |
| |
| // reject different lengths |
| if (b==null || 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[] barray=b.array(); |
| if (barray==null) |
| { |
| for (int i = putIndex(); i-->get;) |
| { |
| byte b1 = _bytes[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; |
| } |
| } |
| } |
| else |
| { |
| for (int i = putIndex(); i-->get;) |
| { |
| byte b1 = _bytes[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; |
| } |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public byte get() |
| { |
| return _bytes[_get++]; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) |
| { |
| int get=getIndex(); |
| for (int i = putIndex(); i-- >get;) |
| { |
| byte b = _bytes[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 byte peek(int index) |
| { |
| return _bytes[index]; |
| } |
| |
| public int peek(int index, byte[] b, int offset, int length) |
| { |
| int l = length; |
| if (index + l > capacity()) |
| { |
| l = capacity() - index; |
| if (l==0) |
| return -1; |
| } |
| |
| if (l < 0) |
| return -1; |
| |
| System.arraycopy(_bytes, index, b, offset, l); |
| return l; |
| } |
| |
| public void poke(int index, byte b) |
| { |
| /* |
| if (isReadOnly()) |
| throw new IllegalStateException(__READONLY); |
| |
| if (index < 0) |
| throw new IllegalArgumentException("index<0: " + index + "<0"); |
| if (index > capacity()) |
| throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity()); |
| */ |
| _bytes[index] = b; |
| } |
| |
| @Override |
| 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(); |
| if (src_array != null) |
| System.arraycopy(src_array, src.getIndex(), _bytes, index, length); |
| else |
| { |
| int s=src.getIndex(); |
| for (int i=0;i<length;i++) |
| _bytes[index++]=src.peek(s++); |
| } |
| |
| return length; |
| } |
| |
| |
| @Override |
| 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()); |
| */ |
| } |
| |
| System.arraycopy(b, offset, _bytes, index, length); |
| |
| return length; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public void writeTo(OutputStream out) |
| throws IOException |
| { |
| out.write(_bytes,getIndex(),length()); |
| if (!isImmutable()) |
| clear(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public int readFrom(InputStream in,int max) throws IOException |
| { |
| if (max<0||max>space()) |
| max=space(); |
| int p = putIndex(); |
| |
| int len=0, total=0, available=max; |
| while (total<max) |
| { |
| len=in.read(_bytes,p,available); |
| if (len<0) |
| break; |
| else if (len>0) |
| { |
| p += len; |
| total += len; |
| available -= len; |
| setPutIndex(p); |
| } |
| if (in.available()<=0) |
| break; |
| } |
| if (len<0 && total==0) |
| return -1; |
| return total; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public int space() |
| { |
| return _bytes.length - _put; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /* ------------------------------------------------------------ */ |
| /* ------------------------------------------------------------ */ |
| public static class CaseInsensitive extends ByteArrayBuffer implements Buffer.CaseInsensitve |
| { |
| public CaseInsensitive(String s) |
| { |
| super(s); |
| } |
| |
| public CaseInsensitive(byte[] b, int o, int l, int rw) |
| { |
| super(b,o,l,rw); |
| } |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| return obj instanceof Buffer && equalsIgnoreCase((Buffer)obj); |
| } |
| |
| } |
| } |