1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.OutputStream;
19import java.io.IOException;
20
21final class ByteStream extends OutputStream {
22    private byte[] buf;
23    private int count;
24
25    public ByteStream() { this(32); }
26
27    public ByteStream(int size) {
28        buf = new byte[size];
29        count = 0;
30    }
31
32    public int getPos() { return count; }
33    public int size() { return count; }
34
35    public void writeBlank(int len) {
36        enlarge(len);
37        count += len;
38    }
39
40    public void write(byte[] data) {
41        write(data, 0, data.length);
42    }
43
44    public void write(byte[] data, int off, int len) {
45        enlarge(len);
46        System.arraycopy(data, off, buf, count, len);
47        count += len;
48    }
49
50    public void write(int b) {
51        enlarge(1);
52        int oldCount = count;
53        buf[oldCount] = (byte)b;
54        count = oldCount + 1;
55    }
56
57    public void writeShort(int s) {
58        enlarge(2);
59        int oldCount = count;
60        buf[oldCount] = (byte)(s >>> 8);
61        buf[oldCount + 1] = (byte)s;
62        count = oldCount + 2;
63    }
64
65    public void writeInt(int i) {
66        enlarge(4);
67        int oldCount = count;
68        buf[oldCount] = (byte)(i >>> 24);
69        buf[oldCount + 1] = (byte)(i >>> 16);
70        buf[oldCount + 2] = (byte)(i >>> 8);
71        buf[oldCount + 3] = (byte)i;
72        count = oldCount + 4;
73    }
74
75    public void writeLong(long i) {
76        enlarge(8);
77        int oldCount = count;
78        buf[oldCount] = (byte)(i >>> 56);
79        buf[oldCount + 1] = (byte)(i >>> 48);
80        buf[oldCount + 2] = (byte)(i >>> 40);
81        buf[oldCount + 3] = (byte)(i >>> 32);
82        buf[oldCount + 4] = (byte)(i >>> 24);
83        buf[oldCount + 5] = (byte)(i >>> 16);
84        buf[oldCount + 6] = (byte)(i >>> 8);
85        buf[oldCount + 7] = (byte)i;
86        count = oldCount + 8;
87    }
88
89    public void writeFloat(float v) {
90        writeInt(Float.floatToIntBits(v));
91    }
92
93    public void writeDouble(double v) {
94        writeLong(Double.doubleToLongBits(v));
95    }
96
97    public void writeUTF(String s) {
98        int sLen = s.length();
99        int pos = count;
100        enlarge(sLen + 2);
101
102        byte[] buffer = buf;
103        buffer[pos++] = (byte)(sLen >>> 8);
104        buffer[pos++] = (byte)sLen;
105        for (int i = 0; i < sLen; ++i) {
106            char c = s.charAt(i);
107            if (0x01 <= c && c <= 0x7f)
108                buffer[pos++] = (byte)c;
109            else {
110                writeUTF2(s, sLen, i);
111                return;
112            }
113        }
114
115        count = pos;
116    }
117
118    private void writeUTF2(String s, int sLen, int offset) {
119        int size = sLen;
120        for (int i = offset; i < sLen; i++) {
121            int c = s.charAt(i);
122            if (c > 0x7ff)
123                size += 2;  // 3 bytes code
124            else if (c == 0 || c > 0x7f)
125                ++size;     // 2 bytes code
126        }
127
128        if (size > 65535)
129            throw new RuntimeException(
130                    "encoded string too long: " + sLen + size + " bytes");
131
132        enlarge(size + 2);
133        int pos = count;
134        byte[] buffer = buf;
135        buffer[pos] = (byte)(size >>> 8);
136        buffer[pos + 1] = (byte)size;
137        pos += 2 + offset;
138        for (int j = offset; j < sLen; ++j) {
139            int c = s.charAt(j);
140            if (0x01 <= c && c <= 0x7f)
141                buffer[pos++] = (byte) c;
142            else if (c > 0x07ff) {
143                buffer[pos] = (byte)(0xe0 | ((c >> 12) & 0x0f));
144                buffer[pos + 1] = (byte)(0x80 | ((c >> 6) & 0x3f));
145                buffer[pos + 2] = (byte)(0x80 | (c & 0x3f));
146                pos += 3;
147            }
148            else {
149                buffer[pos] = (byte)(0xc0 | ((c >> 6) & 0x1f));
150                buffer[pos + 1] = (byte)(0x80 | (c & 0x3f));
151                pos += 2;
152            }
153        }
154
155        count = pos;
156    }
157
158    public void write(int pos, int value) {
159        buf[pos] = (byte)value;
160    }
161
162    public void writeShort(int pos, int value) {
163        buf[pos] = (byte)(value >>> 8);
164        buf[pos + 1] = (byte)value;
165    }
166
167    public void writeInt(int pos, int value) {
168        buf[pos] = (byte)(value >>> 24);
169        buf[pos + 1] = (byte)(value >>> 16);
170        buf[pos + 2] = (byte)(value >>> 8);
171        buf[pos + 3] = (byte)value;
172    }
173
174    public byte[] toByteArray() {
175        byte[] buf2 = new byte[count];
176        System.arraycopy(buf, 0, buf2, 0, count);
177        return buf2;
178    }
179
180    public void writeTo(OutputStream out) throws IOException {
181        out.write(buf, 0, count);
182    }
183
184    public void enlarge(int delta) {
185        int newCount = count + delta;
186        if (newCount > buf.length) {
187            int newLen = buf.length << 1;
188            byte[] newBuf = new byte[newLen > newCount ? newLen : newCount];
189            System.arraycopy(buf, 0, newBuf, 0, count);
190            buf = newBuf;
191        }
192    }
193}
194