/* |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
/* |
|
* This file is available under and governed by the GNU General Public |
|
* License version 2 only, as published by the Free Software Foundation. |
|
* However, the following notice accompanied the original version of this |
|
* file: |
|
* |
|
* 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 jdk.internal.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; |
|
} |
|
} |