| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package macromedia.abc; |
| |
| import macromedia.asc.util.IntegerPool; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * @author Clement Wong |
| */ |
| public class BytecodeBuffer |
| { |
| public BytecodeBuffer(byte[] bytecodes) |
| { |
| this.bytecodes = bytecodes; |
| size = bytecodes.length; |
| pos = 0; |
| } |
| |
| public BytecodeBuffer(String filename) throws IOException |
| { |
| BufferedInputStream in = null; |
| try |
| { |
| in = new BufferedInputStream(new FileInputStream(filename)); |
| bytecodes = new byte[in.available()]; |
| in.read(bytecodes); |
| size = bytecodes.length; |
| pos = 0; |
| } |
| finally |
| { |
| if (in != null) |
| { |
| try |
| { |
| in.close(); |
| } |
| catch (IOException ex) |
| { |
| } |
| } |
| } |
| } |
| |
| public BytecodeBuffer(int preferredSize) |
| { |
| preferredSize = (preferredSize <= 0) ? 1000 : preferredSize; |
| this.bytecodes = new byte[preferredSize]; |
| size = 0; |
| pos = 0; |
| } |
| |
| private byte[] bytecodes; |
| private int pos, size; |
| |
| public void clear() |
| { |
| pos = 0; |
| size = 0; |
| } |
| |
| public int pos() |
| { |
| return pos; |
| } |
| |
| public int size() |
| { |
| return size; |
| } |
| |
| public void delete(int count) |
| { |
| size -= count; |
| } |
| |
| public void writeS8(int v) |
| { |
| writeU8(v); |
| } |
| |
| public void writeU8(int v) |
| { |
| resize(1); |
| bytecodes[size++] = (byte) v; |
| } |
| |
| public void writeU8(int pos, int v) |
| { |
| bytecodes[pos] = (byte) v; |
| } |
| |
| public void writeU16(int v) |
| { |
| resize(2); |
| bytecodes[size++] = (byte) v; |
| bytecodes[size++] = (byte) (v >> 8); |
| } |
| |
| public void writeS24(int v) |
| { |
| writeU24(v); |
| } |
| |
| public void writeS24(int pos, int v) |
| { |
| bytecodes[pos] = (byte) v; |
| bytecodes[pos + 1] = (byte) (v >> 8); |
| bytecodes[pos + 2] = (byte) (v >> 16); |
| } |
| |
| public void writeU24(int v) |
| { |
| resize(3); |
| bytecodes[size++] = (byte) v; |
| bytecodes[size++] = (byte) (v >> 8); |
| bytecodes[size++] = (byte) (v >> 16); |
| } |
| |
| public void writeU32(long v) |
| { |
| if ( v < 128 && v > -1 ) |
| { |
| resize(1); |
| bytecodes[size++] = (byte) v; |
| } |
| else if ( v < 16384 && v > -1) |
| { |
| resize(2); |
| bytecodes[size++] = (byte) ((v & 0x7F) | 0x80); |
| bytecodes[size++] = (byte) ((v >> 7) & 0x7F); |
| } |
| else if ( v < 2097152 && v > -1) |
| { |
| resize(3); |
| bytecodes[size++] = (byte) ((v & 0x7F) | 0x80); |
| bytecodes[size++] = (byte) ((v >> 7) | 0x80); |
| bytecodes[size++] = (byte) ((v >> 14) & 0x7F); |
| } |
| else if ( v < 268435456 && v > -1) |
| { |
| resize(4); |
| bytecodes[size++] = (byte) ((v & 0x7F) | 0x80); |
| bytecodes[size++] = (byte) (v >> 7 | 0x80); |
| bytecodes[size++] = (byte) (v >> 14 | 0x80); |
| bytecodes[size++] = (byte) ((v >> 21) & 0x7F); |
| } |
| else |
| { |
| resize(5); |
| bytecodes[size++] = (byte) ((v & 0x7F) | 0x80); |
| bytecodes[size++] = (byte) (v >> 7 | 0x80); |
| bytecodes[size++] = (byte) (v >> 14 | 0x80); |
| bytecodes[size++] = (byte) (v >> 21 | 0x80); |
| bytecodes[size++] = (byte) ((v >> 28) & 0x0F ); |
| } |
| } |
| |
| public void writeDouble(double v) |
| { |
| resize(8); |
| // todo switch for endianness on Mac |
| long bits = Double.doubleToLongBits(v); |
| bytecodes[size++] = (byte) bits; |
| bytecodes[size++] = (byte) (bits >> 8); |
| bytecodes[size++] = (byte) (bits >> 16); |
| bytecodes[size++] = (byte) (bits >> 24); |
| bytecodes[size++] = (byte) (bits >> 32); |
| bytecodes[size++] = (byte) (bits >> 40); |
| bytecodes[size++] = (byte) (bits >> 48); |
| bytecodes[size++] = (byte) (bits >> 56); |
| } |
| |
| /** |
| * @param start - inclusive |
| * @param end - exclusive |
| */ |
| public void writeBytes(BytecodeBuffer b, int start, int end) |
| { |
| resize(end - start); |
| for (int i = start; i < end; i++) |
| { |
| bytecodes[size++] = b.bytecodes[i]; |
| } |
| } |
| |
| private void resize(int increment) |
| { |
| if (size + increment > bytecodes.length) |
| { |
| byte[] temp = new byte[bytecodes.length * 3 / 2 + 1]; |
| System.arraycopy(bytecodes, 0, temp, 0, bytecodes.length); |
| bytecodes = temp; |
| } |
| } |
| |
| public int readU8() |
| { |
| int value = bytecodes[pos] & 0xff; |
| pos++; |
| return value; |
| } |
| |
| public int readU8(int pos) |
| { |
| return bytecodes[pos] & 0xff; |
| } |
| |
| public int readU16() |
| { |
| return readU8() | (readU8() << 8); |
| } |
| |
| public int readU32() |
| { |
| int result = readU8(); |
| if (0==(result & 0x00000080)) |
| return result; |
| result = result & 0x0000007f | readU8()<<7; |
| if (0==(result & 0x00004000)) |
| return result; |
| result = result & 0x00003fff | readU8()<<14; |
| if (0==(result & 0x00200000)) |
| return result; |
| result = result & 0x001fffff | readU8()<<21; |
| if (0==(result & 0x10000000)) |
| return result; |
| return result & 0x0fffffff | readU8()<<28; |
| } |
| |
| public long readU32(int pos) |
| { |
| int result = readU8(pos++); |
| if (0==(result & 0x00000080)) |
| return result; |
| result = result & 0x0000007f | readU8(pos++)<<7; |
| if (0==(result & 0x00004000)) |
| return result; |
| result = result & 0x00003fff | readU8(pos++)<<14; |
| if (0==(result & 0x00200000)) |
| return result; |
| result = result & 0x001fffff | readU8(pos++)<<21; |
| if (0==(result & 0x10000000)) |
| return result; |
| return result & 0x0fffffff | readU8(pos++)<<28; |
| } |
| |
| public int readS8() |
| { |
| int value = bytecodes[pos]; |
| pos++; |
| return value; |
| } |
| |
| public int readS24() |
| { |
| return readU8() | (readU8() << 8) | (readS8() << 16); |
| } |
| |
| public double readDouble() |
| { |
| long first = readU8() | (readU8() << 8) | (readU8() << 16) | (readU8() << 24); |
| long second = readU8() | (readU8() << 8) | (readU8() << 16) | (readU8() << 24); |
| return Double.longBitsToDouble(first&0xFFFFFFFFL | second<<32); |
| } |
| |
| public byte[] readBytes(int length) |
| { |
| byte[] bytes = new byte[length]; |
| System.arraycopy(bytecodes, pos, bytes, 0, length); |
| pos += length; |
| return bytes; |
| } |
| |
| public String readString(int length) |
| { |
| try |
| { |
| return new String(bytecodes, pos, length, "UTF8"); |
| } |
| catch (UnsupportedEncodingException ex) |
| { |
| return null; |
| } |
| } |
| |
| public void close() |
| { |
| pos = 0; |
| } |
| |
| public void skip(long length) |
| { |
| pos += length; |
| } |
| |
| public void skipEntries(long entries) |
| { |
| for(long i = 0; i < entries; ++i) |
| { |
| readU32(); |
| } |
| } |
| |
| public void seek(int pos) |
| { |
| this.pos = pos; |
| } |
| |
| public boolean same(BytecodeBuffer b, int start1, int end1, int start2, int end2) |
| { |
| if ((end1 - start1) != (end2 - start2)) |
| { |
| return false; |
| } |
| |
| for (int i = start1, j = start2; i < end1;) |
| { |
| if (bytecodes[i] != b.bytecodes[j]) |
| { |
| return false; |
| } |
| |
| i++; |
| j++; |
| } |
| |
| return true; |
| } |
| |
| public int hashCode(int start, int end) |
| { |
| long hash = 1234; |
| |
| for (int j = start; j < end; j++) |
| { |
| hash ^= bytecodes[j]; |
| } |
| |
| return (int) ((hash >> 32) ^ hash); |
| } |
| |
| public void writeTo(OutputStream out) throws IOException |
| { |
| out.write(bytecodes, 0, size); |
| } |
| |
| public int minorVersion() |
| { |
| return (bytecodes[0] & 0xff) | ((bytecodes[1] & 0xff) << 8); |
| } |
| } |
| |
| class ByteArrayPool |
| { |
| ByteArrayPool() |
| { |
| map = new HashMap(); |
| wrappers = new Stack(); |
| key = newByteArray(); |
| } |
| |
| protected Map map; |
| Stack wrappers; |
| private ByteArray key; |
| |
| ByteArray newByteArray() |
| { |
| return new ByteArray(); |
| } |
| |
| int store(BytecodeBuffer b, int start, int end) |
| { |
| ByteArray a = wrappers.isEmpty() ? null : (ByteArray) wrappers.pop(); |
| |
| if (a == null) |
| { |
| a = newByteArray(); |
| } |
| |
| a.clear(); |
| a.b = b; |
| a.start = start; |
| a.end = end; |
| a.init(); |
| |
| Integer index = IntegerPool.getNumber(map.size() + 1); |
| map.put(a, index); |
| |
| return index.intValue(); |
| } |
| |
| int contains(BytecodeBuffer b, int start, int end) |
| { |
| key.clear(); |
| key.b = b; |
| key.start = start; |
| key.end = end; |
| key.hash = 0; |
| key.init(); |
| |
| Integer index = (Integer) map.get(key); |
| return (index != null) ? index.intValue() : -1; |
| } |
| |
| void clear() |
| { |
| for (Iterator i = map.keySet().iterator(); i.hasNext();) |
| { |
| ByteArray a = (ByteArray) i.next(); |
| a.clear(); |
| wrappers.push(a); |
| } |
| |
| map.clear(); |
| } |
| |
| void writeTo(BytecodeBuffer b) |
| { |
| Map sortedMap = new TreeMap(); |
| |
| for (Iterator i = map.keySet().iterator(); i.hasNext();) |
| { |
| Object key = i.next(); // ByteArray |
| Object value = map.get(key); // Integer |
| sortedMap.put(value, key); |
| } |
| |
| b.writeU32((sortedMap.size() == 0) ? 0 : sortedMap.size() + 1); |
| |
| for (Iterator i = sortedMap.keySet().iterator(); i.hasNext();) |
| { |
| Integer index = (Integer) i.next(); |
| ByteArray a = (ByteArray) sortedMap.get(index); |
| b.writeBytes(a.b, a.start, a.end); |
| } |
| } |
| } |
| |
| class ByteArray |
| { |
| BytecodeBuffer b; |
| int start, end, hash; |
| |
| void clear() |
| { |
| b = null; |
| start = 0; |
| end = 0; |
| hash = 0; |
| } |
| |
| void init() |
| { |
| hash = b.hashCode(start, end); |
| } |
| |
| public boolean equals(Object obj) |
| { |
| if (obj instanceof ByteArray) |
| { |
| ByteArray a = (ByteArray) obj; |
| return b.same(a.b, start, end, a.start, a.end); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| public int hashCode() |
| { |
| return hash; |
| } |
| } |
| |