package com.hwlcn.ldap.util; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.Arrays; import com.hwlcn.core.annotation.Mutable; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.asn1.ASN1OctetString; import static com.hwlcn.ldap.util.Debug.*; import static com.hwlcn.ldap.util.UtilityMessages.*; @Mutable() @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class ByteStringBuffer implements Serializable, Appendable { private static final int DEFAULT_INITIAL_CAPACITY = 20; private static final byte[] FALSE_VALUE_BYTES = StaticUtils.getBytes("false"); private static final byte[] TRUE_VALUE_BYTES = StaticUtils.getBytes("true"); private static final ThreadLocal<byte[]> TEMP_NUMBER_BUFFER = new ThreadLocal<byte[]>(); private static final long serialVersionUID = 2899392249591230998L; private byte[] array; private int capacity; private int endPos; public ByteStringBuffer() { this(DEFAULT_INITIAL_CAPACITY); } public ByteStringBuffer(final int initialCapacity) { array = new byte[initialCapacity]; capacity = initialCapacity; endPos = 0; } public ByteStringBuffer append(final boolean b) { if (b) { return append(TRUE_VALUE_BYTES, 0, 4); } else { return append(FALSE_VALUE_BYTES, 0, 5); } } public ByteStringBuffer append(final byte b) { ensureCapacity(endPos + 1); array[endPos++] = b; return this; } public ByteStringBuffer append(final byte[] b) throws NullPointerException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } return append(b, 0, b.length); } public ByteStringBuffer append(final byte[] b, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } if ((off < 0) || (len < 0) || (off+len > b.length)) { final String message; if (off < 0) { message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); } else if (len < 0) { message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); } else { message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, b.length); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } if (len > 0) { ensureCapacity(endPos + len); System.arraycopy(b, off, array, endPos, len); endPos += len; } return this; } public ByteStringBuffer append(final ByteString b) throws NullPointerException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); debugCodingError(e); throw e; } b.appendValueTo(this); return this; } public ByteStringBuffer append(final ByteStringBuffer buffer) throws NullPointerException { if (buffer == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); debugCodingError(e); throw e; } return append(buffer.array, 0, buffer.endPos); } public ByteStringBuffer append(final char c) { final byte b = (byte) (c & 0x7F); if (b == c) { ensureCapacity(endPos + 1); array[endPos++] = b; } else { append(String.valueOf(c)); } return this; } public ByteStringBuffer append(final char[] c) throws NullPointerException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } return append(c, 0, c.length); } public ByteStringBuffer append(final char[] c, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } if ((off < 0) || (len < 0) || (off+len > c.length)) { final String message; if (off < 0) { message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); } else if (len < 0) { message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); } else { message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, c.length); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } if (len > 0) { ensureCapacity(endPos + len); int pos = off; for (int i=0; i < len; i++, pos++) { final byte b = (byte) (c[pos] & 0x7F); if (b == c[pos]) { array[endPos++] = b; } else { append(String.valueOf(c, pos, (off + len - pos))); break; } } } return this; } public ByteStringBuffer append(final CharSequence s) throws NullPointerException { return append(s, 0, s.length()); } public ByteStringBuffer append(final CharSequence s, final int start, final int end) throws NullPointerException, IndexOutOfBoundsException { if (s == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); debugCodingError(e); throw e; } final int length = end - start; ensureCapacity(endPos + length); for (int i=start; i < end; i++) { final char c = s.charAt(i); final byte b = (byte) (c & 0x7F); if (b == c) { array[endPos++] = b; } else { append(StaticUtils.getBytes(s.subSequence(i, length).toString())); break; } } return this; } public ByteStringBuffer append(final int i) { final int length = getBytes(i); return append(TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer append(final long l) { final int length = getBytes(l); return append(TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer insert(final int pos, final boolean b) throws IndexOutOfBoundsException { if (b) { return insert(pos, TRUE_VALUE_BYTES, 0, 4); } else { return insert(pos, FALSE_VALUE_BYTES, 0, 5); } } public ByteStringBuffer insert(final int pos, final byte b) throws IndexOutOfBoundsException { if ((pos < 0) || (pos > endPos)) { final String message; if (pos < 0) { message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); } else { message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } else if (pos == endPos) { return append(b); } ensureCapacity(endPos + 1); System.arraycopy(array, pos, array, pos+1, (endPos-pos)); array[pos] = b; endPos++; return this; } public ByteStringBuffer insert(final int pos, final byte[] b) throws NullPointerException, IndexOutOfBoundsException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } return insert(pos, b, 0, b.length); } public ByteStringBuffer insert(final int pos, final byte[] b, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } if ((pos < 0) || (pos > endPos) || (off < 0) || (len < 0) || (off+len > b.length)) { final String message; if (pos < 0) { message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); } else if (pos > endPos) { message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); } else if (off < 0) { message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); } else if (len < 0) { message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); } else { message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, b.length); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } else if (len == 0) { return this; } else if (pos == endPos) { return append(b, off, len); } ensureCapacity(endPos + len); System.arraycopy(array, pos, array, pos+len, (endPos-pos)); System.arraycopy(b, off, array, pos, len); endPos += len; return this; } public ByteStringBuffer insert(final int pos, final ByteString b) throws NullPointerException, IndexOutOfBoundsException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); debugCodingError(e); throw e; } return insert(pos, b.getValue()); } public ByteStringBuffer insert(final int pos, final ByteStringBuffer buffer) throws NullPointerException, IndexOutOfBoundsException { if (buffer == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); debugCodingError(e); throw e; } return insert(pos, buffer.array, 0, buffer.endPos); } public ByteStringBuffer insert(final int pos, final char c) throws IndexOutOfBoundsException { if ((pos < 0) || (pos > endPos)) { final String message; if (pos < 0) { message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); } else { message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } else if (pos == endPos) { return append(c); } final byte b = (byte) (c & 0x7F); if (b == c) { ensureCapacity(endPos + 1); System.arraycopy(array, pos, array, pos+1, (endPos-pos)); array[pos] = b; endPos++; } else { insert(pos, String.valueOf(c)); } return this; } public ByteStringBuffer insert(final int pos, final char[] c) throws NullPointerException, IndexOutOfBoundsException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } return insert(pos, new String(c, 0, c.length)); } public ByteStringBuffer insert(final int pos, final char[] c, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } return insert(pos, new String(c, off, len)); } public ByteStringBuffer insert(final int pos, final CharSequence s) throws NullPointerException, IndexOutOfBoundsException { if (s == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); debugCodingError(e); throw e; } if ((pos < 0) || (pos > endPos)) { final String message; if (pos < 0) { message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); } else { message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } else if (pos == endPos) { return append(s); } else { return insert(pos, StaticUtils.getBytes(s.toString())); } } public ByteStringBuffer insert(final int pos, final int i) throws IndexOutOfBoundsException { final int length = getBytes(i); return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer insert(final int pos, final long l) throws IndexOutOfBoundsException { final int length = getBytes(l); return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer delete(final int len) throws IndexOutOfBoundsException { return delete(0, len); } public ByteStringBuffer delete(final int off, final int len) throws IndexOutOfBoundsException { if (off < 0) { throw new IndexOutOfBoundsException( ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off)); } else if (len < 0) { throw new IndexOutOfBoundsException( ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len)); } else if ((off + len) > endPos) { throw new IndexOutOfBoundsException( ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, endPos)); } else if (len == 0) { return this; } else if (off == 0) { if (len == endPos) { endPos = 0; return this; } else { final int newEndPos = endPos - len; System.arraycopy(array, len, array, 0, newEndPos); endPos = newEndPos; return this; } } else { if ((off + len) == endPos) { endPos = off; return this; } else { final int bytesToCopy = endPos - (off+len); System.arraycopy(array, (off+len), array, off, bytesToCopy); endPos -= len; return this; } } } public ByteStringBuffer set(final boolean b) { if (b) { return set(TRUE_VALUE_BYTES, 0, 4); } else { return set(FALSE_VALUE_BYTES, 0, 5); } } public ByteStringBuffer set(final byte b) { endPos = 0; return append(b); } public ByteStringBuffer set(final byte[] b) throws NullPointerException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } endPos = 0; return append(b, 0, b.length); } public ByteStringBuffer set(final byte[] b, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } if ((off < 0) || (len < 0) || (off+len > b.length)) { final String message; if (off < 0) { message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); } else if (len < 0) { message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); } else { message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, b.length); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } endPos = 0; return append(b, off, len); } public ByteStringBuffer set(final ByteString b) throws NullPointerException { if (b == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); debugCodingError(e); throw e; } endPos = 0; b.appendValueTo(this); return this; } public ByteStringBuffer set(final ByteStringBuffer buffer) throws NullPointerException { if (buffer == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); debugCodingError(e); throw e; } endPos = 0; return append(buffer.array, 0, buffer.endPos); } public ByteStringBuffer set(final char c) { endPos = 0; return append(c); } public ByteStringBuffer set(final char[] c) throws NullPointerException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } endPos = 0; return append(c, 0, c.length); } public ByteStringBuffer set(final char[] c, final int off, final int len) throws NullPointerException, IndexOutOfBoundsException { if (c == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); debugCodingError(e); throw e; } if ((off < 0) || (len < 0) || (off+len > c.length)) { final String message; if (off < 0) { message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); } else if (len < 0) { message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); } else { message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, c.length); } final IndexOutOfBoundsException e = new IndexOutOfBoundsException(message); debugCodingError(e); throw e; } endPos = 0; return append(c, off, len); } public ByteStringBuffer set(final CharSequence s) throws NullPointerException { if (s == null) { final NullPointerException e = new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); debugCodingError(e); throw e; } endPos = 0; return append(s); } public ByteStringBuffer set(final int i) { final int length = getBytes(i); return set(TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer set(final long l) { final int length = getBytes(l); return set(TEMP_NUMBER_BUFFER.get(), 0, length); } public ByteStringBuffer clear() { endPos = 0; return this; } public ByteStringBuffer clear(final boolean zero) { endPos = 0; if (zero) { Arrays.fill(array, (byte) 0x00); } return this; } public byte[] getBackingArray() { return array; } public boolean isEmpty() { return (endPos == 0); } public int length() { return endPos; } public void setLength(final int length) throws IndexOutOfBoundsException { if (length < 0) { final IndexOutOfBoundsException e = new IndexOutOfBoundsException( ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length)); debugCodingError(e); throw e; } if (length > endPos) { ensureCapacity(length); Arrays.fill(array, endPos, length, (byte) 0x00); endPos = length; } else { endPos = length; } } public int capacity() { return capacity; } public void ensureCapacity(final int minimumCapacity) { if (capacity < minimumCapacity) { final int newCapacity = Math.max(minimumCapacity, (2 * capacity) + 2); final byte[] newArray = new byte[newCapacity]; System.arraycopy(array, 0, newArray, 0, capacity); array = newArray; capacity = newCapacity; } } public void setCapacity(final int capacity) throws IndexOutOfBoundsException { if (capacity < 0) { final IndexOutOfBoundsException e = new IndexOutOfBoundsException( ERR_BS_BUFFER_CAPACITY_NEGATIVE.get(capacity)); debugCodingError(e); throw e; } if (this.capacity == capacity) { return; } else if (this.capacity < capacity) { final byte[] newArray = new byte[capacity]; System.arraycopy(array, 0, newArray, 0, this.capacity); array = newArray; this.capacity = capacity; } else { final byte[] newArray = new byte[capacity]; System.arraycopy(array, 0, newArray, 0, capacity); array = newArray; endPos = Math.min(endPos, capacity); this.capacity = capacity; } } public ByteStringBuffer trimToSize() { if (endPos != capacity) { final byte[] newArray = new byte[endPos]; System.arraycopy(array, 0, newArray, 0, endPos); array = newArray; capacity = endPos; } return this; } public byte[] toByteArray() { final byte[] newArray = new byte[endPos]; System.arraycopy(array, 0, newArray, 0, endPos); return newArray; } public ByteString toByteString() { return new ASN1OctetString(toByteArray()); } public InputStream asInputStream() { return new ByteArrayInputStream(array, 0, endPos); } public void write(final OutputStream outputStream) throws IOException { outputStream.write(array, 0, endPos); } private static int getBytes(final long l) { byte[] b = TEMP_NUMBER_BUFFER.get(); if (b == null) { b = new byte[20]; TEMP_NUMBER_BUFFER.set(b); } if (l == Long.MIN_VALUE) { b[0] = '-'; b[1] = '9'; b[2] = '2'; b[3] = '2'; b[4] = '3'; b[5] = '3'; b[6] = '7'; b[7] = '2'; b[8] = '0'; b[9] = '3'; b[10] = '6'; b[11] = '8'; b[12] = '5'; b[13] = '4'; b[14] = '7'; b[15] = '7'; b[16] = '5'; b[17] = '8'; b[18] = '0'; b[19] = '8'; return 20; } else if (l == 0L) { b[0] = '0'; return 1; } int pos = 0; long v = l; if (l < 0) { b[0] = '-'; pos = 1; v = Math.abs(l); } long divisor; if (v <= 9L) { divisor = 1L; } else if (v <= 99L) { divisor = 10L; } else if (v <= 999L) { divisor = 100L; } else if (v <= 9999L) { divisor = 1000L; } else if (v <= 99999L) { divisor = 10000L; } else if (v <= 999999L) { divisor = 100000L; } else if (v <= 9999999L) { divisor = 1000000L; } else if (v <= 99999999L) { divisor = 10000000L; } else if (v <= 999999999L) { divisor = 100000000L; } else if (v <= 9999999999L) { divisor = 1000000000L; } else if (v <= 99999999999L) { divisor = 10000000000L; } else if (v <= 999999999999L) { divisor = 100000000000L; } else if (v <= 9999999999999L) { divisor = 1000000000000L; } else if (v <= 99999999999999L) { divisor = 10000000000000L; } else if (v <= 999999999999999L) { divisor = 100000000000000L; } else if (v <= 9999999999999999L) { divisor = 1000000000000000L; } else if (v <= 99999999999999999L) { divisor = 10000000000000000L; } else if (v <= 999999999999999999L) { divisor = 100000000000000000L; } else { divisor = 1000000000000000000L; } while (true) { final long digit = v / divisor; switch ((int) digit) { case 0: b[pos++] = '0'; break; case 1: b[pos++] = '1'; break; case 2: b[pos++] = '2'; break; case 3: b[pos++] = '3'; break; case 4: b[pos++] = '4'; break; case 5: b[pos++] = '5'; break; case 6: b[pos++] = '6'; break; case 7: b[pos++] = '7'; break; case 8: b[pos++] = '8'; break; case 9: b[pos++] = '9'; break; } if (divisor == 1L) { break; } else { v -= (divisor * digit); if (v == 0) { while (divisor > 1L) { b[pos++] = '0'; divisor /= 10L; } break; } divisor /= 10L; } } return pos; } @Override() public int hashCode() { int hashCode = 0; for (int i=0; i < endPos; i++) { hashCode += array[i]; } return hashCode; } @Override() public boolean equals(final Object o) { if (o == null) { return false; } if (o == this) { return true; } if (! (o instanceof ByteStringBuffer)) { return false; } final ByteStringBuffer b = (ByteStringBuffer) o; if (endPos != b.endPos) { return false; } for (int i=0; i < endPos; i++) { if (array[i] != b.array[i]) { return false; } } return true; } public ByteStringBuffer duplicate() { final ByteStringBuffer newBuffer = new ByteStringBuffer(endPos); return newBuffer.append(this); } @Override() public String toString() { return StaticUtils.toUTF8String(array, 0, endPos); } }