blob: f36e62d47b7265cb9c185806caacad44dfb6914e [file] [log] [blame]
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.apache.tapestry5.internal.plastic.asm;
/**
* A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream
* on top of a ByteArrayOutputStream, but is more efficient.
*
* @author Eric Bruneton
*/
public class ByteVector {
/** The content of this vector. Only the first {@link #length} bytes contain real data. */
byte[] data;
/** The actual number of bytes in this vector. */
int length;
/** Constructs a new {@link ByteVector} with a default initial capacity. */
public ByteVector() {
data = new byte[64];
}
/**
* Constructs a new {@link ByteVector} with the given initial capacity.
*
* @param initialCapacity the initial capacity of the byte vector to be constructed.
*/
public ByteVector(final int initialCapacity) {
data = new byte[initialCapacity];
}
/**
* Constructs a new {@link ByteVector} from the given initial data.
*
* @param data the initial data of the new byte vector.
*/
ByteVector(final byte[] data) {
this.data = data;
this.length = data.length;
}
/**
* Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
*
* @param byteValue a byte.
* @return this byte vector.
*/
public ByteVector putByte(final int byteValue) {
int currentLength = length;
if (currentLength + 1 > data.length) {
enlarge(1);
}
data[currentLength++] = (byte) byteValue;
length = currentLength;
return this;
}
/**
* Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary.
*
* @param byteValue1 a byte.
* @param byteValue2 another byte.
* @return this byte vector.
*/
final ByteVector put11(final int byteValue1, final int byteValue2) {
int currentLength = length;
if (currentLength + 2 > data.length) {
enlarge(2);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) byteValue1;
currentData[currentLength++] = (byte) byteValue2;
length = currentLength;
return this;
}
/**
* Puts a short into this byte vector. The byte vector is automatically enlarged if necessary.
*
* @param shortValue a short.
* @return this byte vector.
*/
public ByteVector putShort(final int shortValue) {
int currentLength = length;
if (currentLength + 2 > data.length) {
enlarge(2);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) (shortValue >>> 8);
currentData[currentLength++] = (byte) shortValue;
length = currentLength;
return this;
}
/**
* Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if
* necessary.
*
* @param byteValue a byte.
* @param shortValue a short.
* @return this byte vector.
*/
final ByteVector put12(final int byteValue, final int shortValue) {
int currentLength = length;
if (currentLength + 3 > data.length) {
enlarge(3);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) byteValue;
currentData[currentLength++] = (byte) (shortValue >>> 8);
currentData[currentLength++] = (byte) shortValue;
length = currentLength;
return this;
}
/**
* Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if
* necessary.
*
* @param byteValue1 a byte.
* @param byteValue2 another byte.
* @param shortValue a short.
* @return this byte vector.
*/
final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) {
int currentLength = length;
if (currentLength + 4 > data.length) {
enlarge(4);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) byteValue1;
currentData[currentLength++] = (byte) byteValue2;
currentData[currentLength++] = (byte) (shortValue >>> 8);
currentData[currentLength++] = (byte) shortValue;
length = currentLength;
return this;
}
/**
* Puts an int into this byte vector. The byte vector is automatically enlarged if necessary.
*
* @param intValue an int.
* @return this byte vector.
*/
public ByteVector putInt(final int intValue) {
int currentLength = length;
if (currentLength + 4 > data.length) {
enlarge(4);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) (intValue >>> 24);
currentData[currentLength++] = (byte) (intValue >>> 16);
currentData[currentLength++] = (byte) (intValue >>> 8);
currentData[currentLength++] = (byte) intValue;
length = currentLength;
return this;
}
/**
* Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged
* if necessary.
*
* @param byteValue a byte.
* @param shortValue1 a short.
* @param shortValue2 another short.
* @return this byte vector.
*/
final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) {
int currentLength = length;
if (currentLength + 5 > data.length) {
enlarge(5);
}
byte[] currentData = data;
currentData[currentLength++] = (byte) byteValue;
currentData[currentLength++] = (byte) (shortValue1 >>> 8);
currentData[currentLength++] = (byte) shortValue1;
currentData[currentLength++] = (byte) (shortValue2 >>> 8);
currentData[currentLength++] = (byte) shortValue2;
length = currentLength;
return this;
}
/**
* Puts a long into this byte vector. The byte vector is automatically enlarged if necessary.
*
* @param longValue a long.
* @return this byte vector.
*/
public ByteVector putLong(final long longValue) {
int currentLength = length;
if (currentLength + 8 > data.length) {
enlarge(8);
}
byte[] currentData = data;
int intValue = (int) (longValue >>> 32);
currentData[currentLength++] = (byte) (intValue >>> 24);
currentData[currentLength++] = (byte) (intValue >>> 16);
currentData[currentLength++] = (byte) (intValue >>> 8);
currentData[currentLength++] = (byte) intValue;
intValue = (int) longValue;
currentData[currentLength++] = (byte) (intValue >>> 24);
currentData[currentLength++] = (byte) (intValue >>> 16);
currentData[currentLength++] = (byte) (intValue >>> 8);
currentData[currentLength++] = (byte) intValue;
length = currentLength;
return this;
}
/**
* Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
* necessary.
*
* @param stringValue a String whose UTF8 encoded length must be less than 65536.
* @return this byte vector.
*/
// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
public ByteVector putUTF8(final String stringValue) {
int charLength = stringValue.length();
if (charLength > 65535) {
throw new IllegalArgumentException("UTF8 string too large");
}
int currentLength = length;
if (currentLength + 2 + charLength > data.length) {
enlarge(2 + charLength);
}
byte[] currentData = data;
// Optimistic algorithm: instead of computing the byte length and then serializing the string
// (which requires two loops), we assume the byte length is equal to char length (which is the
// most frequent case), and we start serializing the string right away. During the
// serialization, if we find that this assumption is wrong, we continue with the general method.
currentData[currentLength++] = (byte) (charLength >>> 8);
currentData[currentLength++] = (byte) charLength;
for (int i = 0; i < charLength; ++i) {
char charValue = stringValue.charAt(i);
if (charValue >= '\u0001' && charValue <= '\u007F') {
currentData[currentLength++] = (byte) charValue;
} else {
length = currentLength;
return encodeUtf8(stringValue, i, 65535);
}
}
length = currentLength;
return this;
}
/**
* Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
* necessary. The string length is encoded in two bytes before the encoded characters, if there is
* space for that (i.e. if this.length - offset - 2 &gt;= 0).
*
* @param stringValue the String to encode.
* @param offset the index of the first character to encode. The previous characters are supposed
* to have already been encoded, using only one byte per character.
* @param maxByteLength the maximum byte length of the encoded string, including the already
* encoded characters.
* @return this byte vector.
*/
final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) {
int charLength = stringValue.length();
int byteLength = offset;
for (int i = offset; i < charLength; ++i) {
char charValue = stringValue.charAt(i);
if (charValue >= 0x0001 && charValue <= 0x007F) {
byteLength++;
} else if (charValue <= 0x07FF) {
byteLength += 2;
} else {
byteLength += 3;
}
}
if (byteLength > maxByteLength) {
throw new IllegalArgumentException("UTF8 string too large");
}
// Compute where 'byteLength' must be stored in 'data', and store it at this location.
int byteLengthOffset = length - offset - 2;
if (byteLengthOffset >= 0) {
data[byteLengthOffset] = (byte) (byteLength >>> 8);
data[byteLengthOffset + 1] = (byte) byteLength;
}
if (length + byteLength - offset > data.length) {
enlarge(byteLength - offset);
}
int currentLength = length;
for (int i = offset; i < charLength; ++i) {
char charValue = stringValue.charAt(i);
if (charValue >= 0x0001 && charValue <= 0x007F) {
data[currentLength++] = (byte) charValue;
} else if (charValue <= 0x07FF) {
data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
} else {
data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF);
data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F);
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
}
}
length = currentLength;
return this;
}
/**
* Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if
* necessary.
*
* @param byteArrayValue an array of bytes. May be {@literal null} to put {@code byteLength} null
* bytes into this byte vector.
* @param byteOffset index of the first byte of byteArrayValue that must be copied.
* @param byteLength number of bytes of byteArrayValue that must be copied.
* @return this byte vector.
*/
public ByteVector putByteArray(
final byte[] byteArrayValue, final int byteOffset, final int byteLength) {
if (length + byteLength > data.length) {
enlarge(byteLength);
}
if (byteArrayValue != null) {
System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength);
}
length += byteLength;
return this;
}
/**
* Enlarges this byte vector so that it can receive 'size' more bytes.
*
* @param size number of additional bytes that this byte vector should be able to receive.
*/
private void enlarge(final int size) {
int doubleCapacity = 2 * data.length;
int minimalCapacity = length + size;
byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity];
System.arraycopy(data, 0, newData, 0, length);
data = newData;
}
}