package org.jerlang.stdlib.beam_lib; import java.io.DataInputStream; import java.io.IOException; import java.math.BigInteger; import org.jerlang.OpcodeTag; import org.jerlang.stdlib.Lists; import org.jerlang.type.Atom; import org.jerlang.type.Integer; import org.jerlang.type.List; import org.jerlang.type.Term; import org.jerlang.type.Tuple; public class AbstractReader { private DataInputStream inputStream; public AbstractReader(DataInputStream inputStream) { this.inputStream = inputStream; } protected DataInputStream inputStream() { return inputStream; } protected int read1Byte() throws IOException { return inputStream.readUnsignedByte(); } protected int read2Bytes() throws IOException { return inputStream.readUnsignedShort(); } protected int read4Bytes() throws IOException { return inputStream.readInt(); } protected long read8Bytes() throws IOException { return inputStream.readLong(); } protected int readBytes(byte[] bytes) throws IOException { return inputStream.read(bytes); } protected void setInputStream(DataInputStream inputStream) { this.inputStream = inputStream; } protected Term decodeArg(AtomChunk atomChunk, LiteralTableChunk literalTableChunk) throws IOException { int b = read1Byte(); OpcodeTag tag = OpcodeTag.decode(b); switch (tag) { case a: int atomIndex = decodeInt(b).toInt(); if (atomIndex == 0) { return List.nil; } else { return atomChunk.atoms()[atomIndex - 1]; //return Tuple.of(atom, atomChunk.atoms()[atomIndex - 1]); } case i: case u: return decodeInt(b, tag); case z: if ((b & 0x08) != 0) { throw new Error("invalid extended tag: " + b); } switch (b >> 4) { case 0: throw new Error("decode float not implemented yet"); case 1: return decodeList(atomChunk, literalTableChunk); case 2: // float register return Tuple.of(Atom.of("fr"), Integer.of(read1Byte())); case 3: return decodeAllocationList(); case 4: Term litIndex = decodeArg(atomChunk, literalTableChunk); return literalTableChunk.literals().get(litIndex.toInteger().toInt()); default: throw new Error("unsupported z value: " + (b >> 4)); } default: return Tuple.of(tag.toAtom(), decodeInt(b)); } } private Term decodeList(AtomChunk atomChunk, LiteralTableChunk literalTableChunk) throws IOException { List list = List.nil; int elements = decodeInt(read1Byte()).toInt(); while (elements-- > 0) { list = new List(decodeArg(atomChunk, literalTableChunk), list); } return Lists.reverse(list); } private Term decodeAllocationList() throws IOException { List allocationList = List.nil; int elements = decodeInt(read1Byte()).toInt(); while (elements-- > 0) { int typ = decodeInt(read1Byte()).toInt(); int val = decodeInt(read1Byte()).toInt(); switch (typ) { case 0: allocationList = new List(Tuple.of(Atom.of("words"), Integer.of(val)), allocationList); break; case 1: allocationList = new List(Tuple.of(Atom.of("floats"), Integer.of(val)), allocationList); break; case 2: allocationList = new List(Tuple.of(Atom.of("literal"), Integer.of(val)), allocationList); break; } } return allocationList; } protected Integer decodeInt(int b) throws IOException { return decodeInt(b, OpcodeTag.i); } protected Integer decodeInt(int b, OpcodeTag opcodeTag) throws IOException { // N < 16 = 4 bits, NNNN:0:TTT if ((b & 0x08) == 0) { return Integer.of(b >> 4); } // N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN if ((b & 0x10) == 0) { return Integer.of(((b & 0b11100000) << 3) | read1Byte()); } // Bignum int numBytes = decodeIntLength(b); byte[] bytes = new byte[numBytes]; readBytes(bytes); BigInteger n = BigInteger.ZERO; for (byte x : bytes) { n = n.shiftLeft(8); n = n.or(BigInteger.valueOf(x)); } // Negative number if (bytes[0] > 127 && opcodeTag == OpcodeTag.i) { n = n.subtract(BigInteger.ONE.shiftLeft(numBytes)); } return new Integer(n); } private int decodeIntLength(int b) throws IOException { int len = b >>> 5; if (len == 7) { Term term = decodeArg(null, null); if (term instanceof Integer) { return term.toInteger().toInt() + 9; } else { throw new Error("Weird bignum sublength"); } } else { return len + 2; } } }