/* |
|
* 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. |
|
*/ |
|
byte[] data; |
|
/** |
|
* Actual number of bytes in this vector. |
|
*/ |
|
int length; |
|
/** |
|
* Constructs a new {@link ByteVector ByteVector} with a default initial |
|
* size. |
|
*/ |
|
public ByteVector() { |
|
data = new byte[64]; |
|
} |
|
/** |
|
* Constructs a new {@link ByteVector ByteVector} with the given initial |
|
* size. |
|
* |
|
* @param initialSize |
|
* the initial size of the byte vector to be constructed. |
|
*/ |
|
public ByteVector(final int initialSize) { |
|
data = new byte[initialSize]; |
|
} |
|
/** |
|
* Puts a byte into this byte vector. The byte vector is automatically |
|
* enlarged if necessary. |
|
* |
|
* @param b |
|
* a byte. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putByte(final int b) { |
|
int length = this.length; |
|
if (length + 1 > data.length) { |
|
enlarge(1); |
|
} |
|
data[length++] = (byte) b; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts two bytes into this byte vector. The byte vector is automatically |
|
* enlarged if necessary. |
|
* |
|
* @param b1 |
|
* a byte. |
|
* @param b2 |
|
* another byte. |
|
* @return this byte vector. |
|
*/ |
|
ByteVector put11(final int b1, final int b2) { |
|
int length = this.length; |
|
if (length + 2 > data.length) { |
|
enlarge(2); |
|
} |
|
byte[] data = this.data; |
|
data[length++] = (byte) b1; |
|
data[length++] = (byte) b2; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts a short into this byte vector. The byte vector is automatically |
|
* enlarged if necessary. |
|
* |
|
* @param s |
|
* a short. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putShort(final int s) { |
|
int length = this.length; |
|
if (length + 2 > data.length) { |
|
enlarge(2); |
|
} |
|
byte[] data = this.data; |
|
data[length++] = (byte) (s >>> 8); |
|
data[length++] = (byte) s; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts a byte and a short into this byte vector. The byte vector is |
|
* automatically enlarged if necessary. |
|
* |
|
* @param b |
|
* a byte. |
|
* @param s |
|
* a short. |
|
* @return this byte vector. |
|
*/ |
|
ByteVector put12(final int b, final int s) { |
|
int length = this.length; |
|
if (length + 3 > data.length) { |
|
enlarge(3); |
|
} |
|
byte[] data = this.data; |
|
data[length++] = (byte) b; |
|
data[length++] = (byte) (s >>> 8); |
|
data[length++] = (byte) s; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts an int into this byte vector. The byte vector is automatically |
|
* enlarged if necessary. |
|
* |
|
* @param i |
|
* an int. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putInt(final int i) { |
|
int length = this.length; |
|
if (length + 4 > data.length) { |
|
enlarge(4); |
|
} |
|
byte[] data = this.data; |
|
data[length++] = (byte) (i >>> 24); |
|
data[length++] = (byte) (i >>> 16); |
|
data[length++] = (byte) (i >>> 8); |
|
data[length++] = (byte) i; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts a long into this byte vector. The byte vector is automatically |
|
* enlarged if necessary. |
|
* |
|
* @param l |
|
* a long. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putLong(final long l) { |
|
int length = this.length; |
|
if (length + 8 > data.length) { |
|
enlarge(8); |
|
} |
|
byte[] data = this.data; |
|
int i = (int) (l >>> 32); |
|
data[length++] = (byte) (i >>> 24); |
|
data[length++] = (byte) (i >>> 16); |
|
data[length++] = (byte) (i >>> 8); |
|
data[length++] = (byte) i; |
|
i = (int) l; |
|
data[length++] = (byte) (i >>> 24); |
|
data[length++] = (byte) (i >>> 16); |
|
data[length++] = (byte) (i >>> 8); |
|
data[length++] = (byte) i; |
|
this.length = length; |
|
return this; |
|
} |
|
/** |
|
* Puts an UTF8 string into this byte vector. The byte vector is |
|
* automatically enlarged if necessary. |
|
* |
|
* @param s |
|
* a String whose UTF8 encoded length must be less than 65536. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putUTF8(final String s) { |
|
int charLength = s.length(); |
|
if (charLength > 65535) { |
|
throw new IllegalArgumentException(); |
|
} |
|
int len = length; |
|
if (len + 2 + charLength > data.length) { |
|
enlarge(2 + charLength); |
|
} |
|
byte[] data = this.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. |
|
data[len++] = (byte) (charLength >>> 8); |
|
data[len++] = (byte) charLength; |
|
for (int i = 0; i < charLength; ++i) { |
|
char c = s.charAt(i); |
|
if (c >= '\001' && c <= '\177') { |
|
data[len++] = (byte) c; |
|
} else { |
|
length = len; |
|
return encodeUTF8(s, i, 65535); |
|
} |
|
} |
|
length = len; |
|
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 - i - 2 >= 0). |
|
* |
|
* @param s |
|
* the String to encode. |
|
* @param i |
|
* 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. |
|
*/ |
|
ByteVector encodeUTF8(final String s, int i, int maxByteLength) { |
|
int charLength = s.length(); |
|
int byteLength = i; |
|
char c; |
|
for (int j = i; j < charLength; ++j) { |
|
c = s.charAt(j); |
|
if (c >= '\001' && c <= '\177') { |
|
byteLength++; |
|
} else if (c > '\u07FF') { |
|
byteLength += 3; |
|
} else { |
|
byteLength += 2; |
|
} |
|
} |
|
if (byteLength > maxByteLength) { |
|
throw new IllegalArgumentException(); |
|
} |
|
int start = length - i - 2; |
|
if (start >= 0) { |
|
data[start] = (byte) (byteLength >>> 8); |
|
data[start + 1] = (byte) byteLength; |
|
} |
|
if (length + byteLength - i > data.length) { |
|
enlarge(byteLength - i); |
|
} |
|
int len = length; |
|
for (int j = i; j < charLength; ++j) { |
|
c = s.charAt(j); |
|
if (c >= '\001' && c <= '\177') { |
|
data[len++] = (byte) c; |
|
} else if (c > '\u07FF') { |
|
data[len++] = (byte) (0xE0 | c >> 12 & 0xF); |
|
data[len++] = (byte) (0x80 | c >> 6 & 0x3F); |
|
data[len++] = (byte) (0x80 | c & 0x3F); |
|
} else { |
|
data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); |
|
data[len++] = (byte) (0x80 | c & 0x3F); |
|
} |
|
} |
|
length = len; |
|
return this; |
|
} |
|
/** |
|
* Puts an array of bytes into this byte vector. The byte vector is |
|
* automatically enlarged if necessary. |
|
* |
|
* @param b |
|
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt> |
|
* null bytes into this byte vector. |
|
* @param off |
|
* index of the fist byte of b that must be copied. |
|
* @param len |
|
* number of bytes of b that must be copied. |
|
* @return this byte vector. |
|
*/ |
|
public ByteVector putByteArray(final byte[] b, final int off, final int len) { |
|
if (length + len > data.length) { |
|
enlarge(len); |
|
} |
|
if (b != null) { |
|
System.arraycopy(b, off, data, length, len); |
|
} |
|
length += len; |
|
return this; |
|
} |
|
/** |
|
* Enlarge this byte vector so that it can receive n more bytes. |
|
* |
|
* @param size |
|
* number of additional bytes that this byte vector should be |
|
* able to receive. |
|
*/ |
|
private void enlarge(final int size) { |
|
int length1 = 2 * data.length; |
|
int length2 = length + size; |
|
byte[] newData = new byte[length1 > length2 ? length1 : length2]; |
|
System.arraycopy(data, 0, newData, 0, length); |
|
data = newData; |
|
} |
|
} |