Java tutorial
// 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; } }