rlm@10: /***
rlm@10: * ASM: a very small and fast Java bytecode manipulation framework
rlm@10: * Copyright (c) 2000-2005 INRIA, France Telecom
rlm@10: * All rights reserved.
rlm@10: *
rlm@10: * Redistribution and use in source and binary forms, with or without
rlm@10: * modification, are permitted provided that the following conditions
rlm@10: * are met:
rlm@10: * 1. Redistributions of source code must retain the above copyright
rlm@10: * notice, this list of conditions and the following disclaimer.
rlm@10: * 2. Redistributions in binary form must reproduce the above copyright
rlm@10: * notice, this list of conditions and the following disclaimer in the
rlm@10: * documentation and/or other materials provided with the distribution.
rlm@10: * 3. Neither the name of the copyright holders nor the names of its
rlm@10: * contributors may be used to endorse or promote products derived from
rlm@10: * this software without specific prior written permission.
rlm@10: *
rlm@10: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
rlm@10: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
rlm@10: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
rlm@10: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
rlm@10: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
rlm@10: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
rlm@10: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
rlm@10: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
rlm@10: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
rlm@10: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
rlm@10: * THE POSSIBILITY OF SUCH DAMAGE.
rlm@10: */
rlm@10: package clojure.asm;
rlm@10:
rlm@10: /**
rlm@10: * A dynamically extensible vector of bytes. This class is roughly equivalent to
rlm@10: * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
rlm@10: *
rlm@10: * @author Eric Bruneton
rlm@10: */
rlm@10: public class ByteVector{
rlm@10:
rlm@10: /**
rlm@10: * The content of this vector.
rlm@10: */
rlm@10: byte[] data;
rlm@10:
rlm@10: /**
rlm@10: * Actual number of bytes in this vector.
rlm@10: */
rlm@10: int length;
rlm@10:
rlm@10: /**
rlm@10: * Constructs a new {@link ByteVector ByteVector} with a default initial
rlm@10: * size.
rlm@10: */
rlm@10: public ByteVector(){
rlm@10: data = new byte[64];
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Constructs a new {@link ByteVector ByteVector} with the given initial
rlm@10: * size.
rlm@10: *
rlm@10: * @param initialSize the initial size of the byte vector to be constructed.
rlm@10: */
rlm@10: public ByteVector(final int initialSize){
rlm@10: data = new byte[initialSize];
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts a byte into this byte vector. The byte vector is automatically
rlm@10: * enlarged if necessary.
rlm@10: *
rlm@10: * @param b a byte.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putByte(final int b){
rlm@10: int length = this.length;
rlm@10: if(length + 1 > data.length)
rlm@10: {
rlm@10: enlarge(1);
rlm@10: }
rlm@10: data[length++] = (byte) b;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts two bytes into this byte vector. The byte vector is automatically
rlm@10: * enlarged if necessary.
rlm@10: *
rlm@10: * @param b1 a byte.
rlm@10: * @param b2 another byte.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: ByteVector put11(final int b1, final int b2){
rlm@10: int length = this.length;
rlm@10: if(length + 2 > data.length)
rlm@10: {
rlm@10: enlarge(2);
rlm@10: }
rlm@10: byte[] data = this.data;
rlm@10: data[length++] = (byte) b1;
rlm@10: data[length++] = (byte) b2;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts a short into this byte vector. The byte vector is automatically
rlm@10: * enlarged if necessary.
rlm@10: *
rlm@10: * @param s a short.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putShort(final int s){
rlm@10: int length = this.length;
rlm@10: if(length + 2 > data.length)
rlm@10: {
rlm@10: enlarge(2);
rlm@10: }
rlm@10: byte[] data = this.data;
rlm@10: data[length++] = (byte) (s >>> 8);
rlm@10: data[length++] = (byte) s;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts a byte and a short into this byte vector. The byte vector is
rlm@10: * automatically enlarged if necessary.
rlm@10: *
rlm@10: * @param b a byte.
rlm@10: * @param s a short.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: ByteVector put12(final int b, final int s){
rlm@10: int length = this.length;
rlm@10: if(length + 3 > data.length)
rlm@10: {
rlm@10: enlarge(3);
rlm@10: }
rlm@10: byte[] data = this.data;
rlm@10: data[length++] = (byte) b;
rlm@10: data[length++] = (byte) (s >>> 8);
rlm@10: data[length++] = (byte) s;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts an int into this byte vector. The byte vector is automatically
rlm@10: * enlarged if necessary.
rlm@10: *
rlm@10: * @param i an int.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putInt(final int i){
rlm@10: int length = this.length;
rlm@10: if(length + 4 > data.length)
rlm@10: {
rlm@10: enlarge(4);
rlm@10: }
rlm@10: byte[] data = this.data;
rlm@10: data[length++] = (byte) (i >>> 24);
rlm@10: data[length++] = (byte) (i >>> 16);
rlm@10: data[length++] = (byte) (i >>> 8);
rlm@10: data[length++] = (byte) i;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts a long into this byte vector. The byte vector is automatically
rlm@10: * enlarged if necessary.
rlm@10: *
rlm@10: * @param l a long.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putLong(final long l){
rlm@10: int length = this.length;
rlm@10: if(length + 8 > data.length)
rlm@10: {
rlm@10: enlarge(8);
rlm@10: }
rlm@10: byte[] data = this.data;
rlm@10: int i = (int) (l >>> 32);
rlm@10: data[length++] = (byte) (i >>> 24);
rlm@10: data[length++] = (byte) (i >>> 16);
rlm@10: data[length++] = (byte) (i >>> 8);
rlm@10: data[length++] = (byte) i;
rlm@10: i = (int) l;
rlm@10: data[length++] = (byte) (i >>> 24);
rlm@10: data[length++] = (byte) (i >>> 16);
rlm@10: data[length++] = (byte) (i >>> 8);
rlm@10: data[length++] = (byte) i;
rlm@10: this.length = length;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts an UTF8 string into this byte vector. The byte vector is
rlm@10: * automatically enlarged if necessary.
rlm@10: *
rlm@10: * @param s a String.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putUTF8(final String s){
rlm@10: int charLength = s.length();
rlm@10: if(length + 2 + charLength > data.length)
rlm@10: {
rlm@10: enlarge(2 + charLength);
rlm@10: }
rlm@10: int len = length;
rlm@10: byte[] data = this.data;
rlm@10: // optimistic algorithm: instead of computing the byte length and then
rlm@10: // serializing the string (which requires two loops), we assume the byte
rlm@10: // length is equal to char length (which is the most frequent case), and
rlm@10: // we start serializing the string right away. During the serialization,
rlm@10: // if we find that this assumption is wrong, we continue with the
rlm@10: // general method.
rlm@10: data[len++] = (byte) (charLength >>> 8);
rlm@10: data[len++] = (byte) charLength;
rlm@10: for(int i = 0; i < charLength; ++i)
rlm@10: {
rlm@10: char c = s.charAt(i);
rlm@10: if(c >= '\001' && c <= '\177')
rlm@10: {
rlm@10: data[len++] = (byte) c;
rlm@10: }
rlm@10: else
rlm@10: {
rlm@10: int byteLength = i;
rlm@10: for(int j = i; j < charLength; ++j)
rlm@10: {
rlm@10: c = s.charAt(j);
rlm@10: if(c >= '\001' && c <= '\177')
rlm@10: {
rlm@10: byteLength++;
rlm@10: }
rlm@10: else if(c > '\u07FF')
rlm@10: {
rlm@10: byteLength += 3;
rlm@10: }
rlm@10: else
rlm@10: {
rlm@10: byteLength += 2;
rlm@10: }
rlm@10: }
rlm@10: data[length] = (byte) (byteLength >>> 8);
rlm@10: data[length + 1] = (byte) byteLength;
rlm@10: if(length + 2 + byteLength > data.length)
rlm@10: {
rlm@10: length = len;
rlm@10: enlarge(2 + byteLength);
rlm@10: data = this.data;
rlm@10: }
rlm@10: for(int j = i; j < charLength; ++j)
rlm@10: {
rlm@10: c = s.charAt(j);
rlm@10: if(c >= '\001' && c <= '\177')
rlm@10: {
rlm@10: data[len++] = (byte) c;
rlm@10: }
rlm@10: else if(c > '\u07FF')
rlm@10: {
rlm@10: data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
rlm@10: data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
rlm@10: data[len++] = (byte) (0x80 | c & 0x3F);
rlm@10: }
rlm@10: else
rlm@10: {
rlm@10: data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
rlm@10: data[len++] = (byte) (0x80 | c & 0x3F);
rlm@10: }
rlm@10: }
rlm@10: break;
rlm@10: }
rlm@10: }
rlm@10: length = len;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Puts an array of bytes into this byte vector. The byte vector is
rlm@10: * automatically enlarged if necessary.
rlm@10: *
rlm@10: * @param b an array of bytes. May be null to put len
rlm@10: * null bytes into this byte vector.
rlm@10: * @param off index of the fist byte of b that must be copied.
rlm@10: * @param len number of bytes of b that must be copied.
rlm@10: * @return this byte vector.
rlm@10: */
rlm@10: public ByteVector putByteArray(final byte[] b, final int off, final int len){
rlm@10: if(length + len > data.length)
rlm@10: {
rlm@10: enlarge(len);
rlm@10: }
rlm@10: if(b != null)
rlm@10: {
rlm@10: System.arraycopy(b, off, data, length, len);
rlm@10: }
rlm@10: length += len;
rlm@10: return this;
rlm@10: }
rlm@10:
rlm@10: /**
rlm@10: * Enlarge this byte vector so that it can receive n more bytes.
rlm@10: *
rlm@10: * @param size number of additional bytes that this byte vector should be
rlm@10: * able to receive.
rlm@10: */
rlm@10: private void enlarge(final int size){
rlm@10: int length1 = 2 * data.length;
rlm@10: int length2 = length + size;
rlm@10: byte[] newData = new byte[length1 > length2 ? length1 : length2];
rlm@10: System.arraycopy(data, 0, newData, 0, length);
rlm@10: data = newData;
rlm@10: }
rlm@10: }