// 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.objectweb.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 >= 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;
}
}