aboutsummaryrefslogtreecommitdiffstats
path: root/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java')
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
new file mode 100644
index 0000000000..b9b43481ee
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -0,0 +1,1203 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+package com.ericsson.otp.erlang;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+/**
+ * Provides a stream for decoding Erlang terms from external format.
+ *
+ * <p>
+ * Note that this class is not synchronized, if you need synchronization you
+ * must provide it yourself.
+ */
+public class OtpInputStream extends ByteArrayInputStream {
+
+ public static int DECODE_INT_LISTS_AS_STRINGS = 1;
+
+ private final int flags;
+
+ /**
+ * @param buf
+ */
+ public OtpInputStream(final byte[] buf) {
+ this(buf, 0);
+ }
+
+ /**
+ * Create a stream from a buffer containing encoded Erlang terms.
+ *
+ * @param flags
+ */
+ public OtpInputStream(final byte[] buf, final int flags) {
+ super(buf);
+ this.flags = flags;
+ }
+
+ /**
+ * Create a stream from a buffer containing encoded Erlang terms at the
+ * given offset and length.
+ *
+ * @param flags
+ */
+ public OtpInputStream(final byte[] buf, final int offset, final int length,
+ final int flags) {
+ super(buf, offset, length);
+ this.flags = flags;
+ }
+
+ /**
+ * Get the current position in the stream.
+ *
+ * @return the current position in the stream.
+ */
+ public int getPos() {
+ return super.pos;
+ }
+
+ /**
+ * Set the current position in the stream.
+ *
+ * @param pos
+ * the position to move to in the stream. If pos indicates a
+ * position beyond the end of the stream, the position is move to
+ * the end of the stream instead. If pos is negative, the
+ * position is moved to the beginning of the stream instead.
+ *
+ * @return the previous position in the stream.
+ */
+ public int setPos(int pos) {
+ final int oldpos = super.pos;
+
+ if (pos > super.count) {
+ pos = super.count;
+ } else if (pos < 0) {
+ pos = 0;
+ }
+
+ super.pos = pos;
+
+ return oldpos;
+ }
+
+ /**
+ * Read an array of bytes from the stream. The method reads at most
+ * buf.length bytes from the input stream.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int readN(final byte[] buf) throws OtpErlangDecodeException {
+ return this.readN(buf, 0, buf.length);
+ }
+
+ /**
+ * Read an array of bytes from the stream. The method reads at most len
+ * bytes from the input stream into offset off of the buffer.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int readN(final byte[] buf, final int off, final int len)
+ throws OtpErlangDecodeException {
+ if (len == 0 && available() == 0) {
+ return 0;
+ }
+ final int i = super.read(buf, off, len);
+ if (i < 0) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ return i;
+ }
+
+ /**
+ * Alias for peek1()
+ */
+ public int peek() throws OtpErlangDecodeException {
+ return peek1();
+ }
+
+ /**
+ * Look ahead one position in the stream without consuming the byte found
+ * there.
+ *
+ * @return the next byte in the stream, as an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int peek1() throws OtpErlangDecodeException {
+ int i;
+ try {
+ i = super.buf[super.pos];
+ if (i < 0) {
+ i += 256;
+ }
+
+ return i;
+ } catch (final Exception e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ }
+
+ public int peek1skip_version() throws OtpErlangDecodeException {
+ int i = peek1();
+ if (i == OtpExternal.versionTag) {
+ read1();
+ i = peek1();
+ }
+ return i;
+ }
+
+ /**
+ * Read a one byte integer from the stream.
+ *
+ * @return the byte read, as an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read1() throws OtpErlangDecodeException {
+ int i;
+ i = super.read();
+
+ if (i < 0) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+
+ return i;
+ }
+
+ public int read1skip_version() throws OtpErlangDecodeException {
+ int tag = read1();
+ if (tag == OtpExternal.versionTag) {
+ tag = read1();
+ }
+ return tag;
+ }
+
+ /**
+ * Read a two byte big endian integer from the stream.
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read2BE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[2];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[0] << 8 & 0xff00) + (b[1] & 0xff);
+ }
+
+ /**
+ * Read a four byte big endian integer from the stream.
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read4BE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[4];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[0] << 24 & 0xff000000) + (b[1] << 16 & 0xff0000)
+ + (b[2] << 8 & 0xff00) + (b[3] & 0xff);
+ }
+
+ /**
+ * Read a two byte little endian integer from the stream.
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read2LE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[2];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[1] << 8 & 0xff00) + (b[0] & 0xff);
+ }
+
+ /**
+ * Read a four byte little endian integer from the stream.
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read4LE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[4];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[3] << 24 & 0xff000000) + (b[2] << 16 & 0xff0000)
+ + (b[1] << 8 & 0xff00) + (b[0] & 0xff);
+ }
+
+ /**
+ * Read a little endian integer from the stream.
+ *
+ * @param n
+ * the number of bytes to read
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public long readLE(int n) throws OtpErlangDecodeException {
+ final byte[] b = new byte[n];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ long v = 0;
+ while (n-- > 0) {
+ v = v << 8 | (long) b[n] & 0xff;
+ }
+ return v;
+ }
+
+ /**
+ * Read a bigendian integer from the stream.
+ *
+ * @param n
+ * the number of bytes to read
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public long readBE(final int n) throws OtpErlangDecodeException {
+ final byte[] b = new byte[n];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ long v = 0;
+ for (int i = 0; i < n; i++) {
+ v = v << 8 | (long) b[i] & 0xff;
+ }
+ return v;
+ }
+
+ /**
+ * Read an Erlang atom from the stream and interpret the value as a boolean.
+ *
+ * @return true if the atom at the current position in the stream contains
+ * the value 'true' (ignoring case), false otherwise.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an atom.
+ */
+ public boolean read_boolean() throws OtpErlangDecodeException {
+ return Boolean.valueOf(read_atom()).booleanValue();
+ }
+
+ /**
+ * Read an Erlang atom from the stream.
+ *
+ * @return a String containing the value of the atom.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an atom.
+ */
+ public String read_atom() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] strbuf;
+ String atom;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.atomTag) {
+ throw new OtpErlangDecodeException(
+ "wrong tag encountered, expected " + OtpExternal.atomTag
+ + ", got " + tag);
+ }
+
+ len = read2BE();
+
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ atom = OtpErlangString.newString(strbuf);
+
+ if (atom.length() > OtpExternal.maxAtomLength) {
+ atom = atom.substring(0, OtpExternal.maxAtomLength);
+ }
+
+ return atom;
+ }
+
+ /**
+ * Read an Erlang binary from the stream.
+ *
+ * @return a byte array containing the value of the binary.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a binary.
+ */
+ public byte[] read_binary() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] bin;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.binTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.binTag
+ + ", got " + tag);
+ }
+
+ len = read4BE();
+
+ bin = new byte[len];
+ this.readN(bin);
+
+ return bin;
+ }
+
+ /**
+ * Read an Erlang bitstr from the stream.
+ *
+ * @param pad_bits
+ * an int array whose first element will be set to the number of
+ * pad bits in the last byte.
+ *
+ * @return a byte array containing the value of the bitstr.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a bitstr.
+ */
+ public byte[] read_bitstr(final int pad_bits[])
+ throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] bin;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.bitBinTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.bitBinTag
+ + ", got " + tag);
+ }
+
+ len = read4BE();
+ bin = new byte[len];
+ final int tail_bits = read1();
+ if (tail_bits < 0 || 7 < tail_bits) {
+ throw new OtpErlangDecodeException(
+ "Wrong tail bit count in bitstr: " + tail_bits);
+ }
+ if (len == 0 && tail_bits != 0) {
+ throw new OtpErlangDecodeException(
+ "Length 0 on bitstr with tail bit count: " + tail_bits);
+ }
+ this.readN(bin);
+
+ pad_bits[0] = 8 - tail_bits;
+ return bin;
+ }
+
+ /**
+ * Read an Erlang float from the stream.
+ *
+ * @return the float value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a float.
+ */
+ public float read_float() throws OtpErlangDecodeException {
+ final double d = read_double();
+ return (float) d;
+ }
+
+ /**
+ * Read an Erlang float from the stream.
+ *
+ * @return the float value, as a double.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a float.
+ */
+ public double read_double() throws OtpErlangDecodeException {
+ int tag;
+
+ // parse the stream
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.newFloatTag: {
+ return Double.longBitsToDouble(readBE(8));
+ }
+ case OtpExternal.floatTag: {
+ BigDecimal val;
+ int epos;
+ int exp;
+ final byte[] strbuf = new byte[31];
+ String str;
+
+ // get the string
+ this.readN(strbuf);
+ str = OtpErlangString.newString(strbuf);
+
+ // find the exponent prefix 'e' in the string
+ epos = str.indexOf('e', 0);
+
+ if (epos < 0) {
+ throw new OtpErlangDecodeException("Invalid float format: '"
+ + str + "'");
+ }
+
+ // remove the sign from the exponent, if positive
+ String estr = str.substring(epos + 1).trim();
+
+ if (estr.substring(0, 1).equals("+")) {
+ estr = estr.substring(1);
+ }
+
+ // now put the mantissa and exponent together
+ exp = Integer.valueOf(estr).intValue();
+ val = new BigDecimal(str.substring(0, epos)).movePointRight(exp);
+
+ return val.doubleValue();
+ }
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected "
+ + OtpExternal.newFloatTag + ", got " + tag);
+ }
+ }
+
+ /**
+ * Read one byte from the stream.
+ *
+ * @return the byte read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public byte read_byte() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final byte i = (byte) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in byte: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read a character from the stream.
+ *
+ * @return the character value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an integer that can
+ * be represented as a char.
+ */
+ public char read_char() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final char i = (char) l;
+
+ if (l != (i & 0xffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in char: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned integer from the stream.
+ *
+ * @return the integer value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive integer.
+ */
+ public int read_uint() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final int i = (int) l;
+
+ if (l != (i & 0xFFFFffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in uint: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an integer from the stream.
+ *
+ * @return the integer value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as
+ * an integer.
+ */
+ public int read_int() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final int i = (int) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in int: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned short from the stream.
+ *
+ * @return the short value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive short.
+ */
+ public short read_ushort() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final short i = (short) l;
+
+ if (l != (i & 0xffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in ushort: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read a short from the stream.
+ *
+ * @return the short value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * short.
+ */
+ public short read_short() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final short i = (short) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in short: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned long from the stream.
+ *
+ * @return the long value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive long.
+ */
+ public long read_ulong() throws OtpErlangDecodeException {
+ return this.read_long(true);
+ }
+
+ /**
+ * Read a long from the stream.
+ *
+ * @return the long value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * long.
+ */
+ public long read_long() throws OtpErlangDecodeException {
+ return this.read_long(false);
+ }
+
+ public long read_long(final boolean unsigned)
+ throws OtpErlangDecodeException {
+ final byte[] b = read_integer_byte_array();
+ return OtpInputStream.byte_array_to_long(b, unsigned);
+ }
+
+ /**
+ * Read an integer from the stream.
+ *
+ * @return the value as a big endian 2's complement byte array.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an integer.
+ */
+ public byte[] read_integer_byte_array() throws OtpErlangDecodeException {
+ int tag;
+ byte[] nb;
+
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.smallIntTag:
+ nb = new byte[2];
+ nb[0] = 0;
+ nb[1] = (byte) read1();
+ break;
+
+ case OtpExternal.intTag:
+ nb = new byte[4];
+ if (this.readN(nb) != 4) { // Big endian
+ throw new OtpErlangDecodeException(
+ "Cannot read from intput stream");
+ }
+ break;
+
+ case OtpExternal.smallBigTag:
+ case OtpExternal.largeBigTag:
+ int arity;
+ int sign;
+ if (tag == OtpExternal.smallBigTag) {
+ arity = read1();
+ sign = read1();
+ } else {
+ arity = read4BE();
+ sign = read1();
+ if (arity + 1 < 0) {
+ throw new OtpErlangDecodeException(
+ "Value of largeBig does not fit in BigInteger, arity "
+ + arity + " sign " + sign);
+ }
+ }
+ nb = new byte[arity + 1];
+ // Value is read as little endian. The big end is augumented
+ // with one zero byte to make the value 2's complement positive.
+ if (this.readN(nb, 0, arity) != arity) {
+ throw new OtpErlangDecodeException(
+ "Cannot read from intput stream");
+ }
+ // Reverse the array to make it big endian.
+ for (int i = 0, j = nb.length; i < j--; i++) {
+ // Swap [i] with [j]
+ final byte b = nb[i];
+ nb[i] = nb[j];
+ nb[j] = b;
+ }
+ if (sign != 0) {
+ // 2's complement negate the big endian value in the array
+ int c = 1; // Carry
+ for (int j = nb.length; j-- > 0;) {
+ c = (~nb[j] & 0xFF) + c;
+ nb[j] = (byte) c;
+ c >>= 8;
+ }
+ }
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid integer tag: " + tag);
+ }
+
+ return nb;
+ }
+
+ public static long byte_array_to_long(final byte[] b, final boolean unsigned)
+ throws OtpErlangDecodeException {
+ long v;
+ switch (b.length) {
+ case 0:
+ v = 0;
+ break;
+ case 2:
+ v = ((b[0] & 0xFF) << 8) + (b[1] & 0xFF);
+ v = (short) v; // Sign extend
+ if (v < 0 && unsigned) {
+ throw new OtpErlangDecodeException("Value not unsigned: " + v);
+ }
+ break;
+ case 4:
+ v = ((b[0] & 0xFF) << 24) + ((b[1] & 0xFF) << 16)
+ + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
+ v = (int) v; // Sign extend
+ if (v < 0 && unsigned) {
+ throw new OtpErlangDecodeException("Value not unsigned: " + v);
+ }
+ break;
+ default:
+ int i = 0;
+ final byte c = b[i];
+ // Skip non-essential leading bytes
+ if (unsigned) {
+ if (c < 0) {
+ throw new OtpErlangDecodeException("Value not unsigned: "
+ + b);
+ }
+ while (b[i] == 0) {
+ i++; // Skip leading zero sign bytes
+ }
+ } else {
+ if (c == 0 || c == -1) { // Leading sign byte
+ i = 1;
+ // Skip all leading sign bytes
+ while (i < b.length && b[i] == c) {
+ i++;
+ }
+ if (i < b.length) {
+ // Check first non-sign byte to see if its sign
+ // matches the whole number's sign. If not one more
+ // byte is needed to represent the value.
+ if (((c ^ b[i]) & 0x80) != 0) {
+ i--;
+ }
+ }
+ }
+ }
+ if (b.length - i > 8) {
+ // More than 64 bits of value
+ throw new OtpErlangDecodeException(
+ "Value does not fit in long: " + b);
+ }
+ // Convert the necessary bytes
+ for (v = c < 0 ? -1 : 0; i < b.length; i++) {
+ v = v << 8 | b[i] & 0xFF;
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Read a list header from the stream.
+ *
+ * @return the arity of the list.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a list.
+ */
+ public int read_list_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.nilTag:
+ arity = 0;
+ break;
+
+ case OtpExternal.stringTag:
+ arity = read2BE();
+ break;
+
+ case OtpExternal.listTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid list tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read a tuple header from the stream.
+ *
+ * @return the arity of the tuple.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a tuple.
+ */
+ public int read_tuple_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ // decode the tuple header and get arity
+ switch (tag) {
+ case OtpExternal.smallTupleTag:
+ arity = read1();
+ break;
+
+ case OtpExternal.largeTupleTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid tuple tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read an empty list from the stream.
+ *
+ * @return zero (the arity of the list).
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an empty list.
+ */
+ public int read_nil() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.nilTag:
+ arity = 0;
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid nil tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read an Erlang PID from the stream.
+ *
+ * @return the value of the PID.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang PID.
+ */
+ public OtpErlangPid read_pid() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int serial;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.pidTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.pidTag
+ + ", got " + tag);
+ }
+
+ node = read_atom();
+ id = read4BE() & 0x7fff; // 15 bits
+ serial = read4BE() & 0x1fff; // 13 bits
+ creation = read1() & 0x03; // 2 bits
+
+ return new OtpErlangPid(node, id, serial, creation);
+ }
+
+ /**
+ * Read an Erlang port from the stream.
+ *
+ * @return the value of the port.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang port.
+ */
+ public OtpErlangPort read_port() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.portTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.portTag
+ + ", got " + tag);
+ }
+
+ node = read_atom();
+ id = read4BE() & 0xfffffff; // 28 bits
+ creation = read1() & 0x03; // 2 bits
+
+ return new OtpErlangPort(node, id, creation);
+ }
+
+ /**
+ * Read an Erlang reference from the stream.
+ *
+ * @return the value of the reference
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang reference.
+ */
+ public OtpErlangRef read_ref() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.refTag:
+ node = read_atom();
+ id = read4BE() & 0x3ffff; // 18 bits
+ creation = read1() & 0x03; // 2 bits
+ return new OtpErlangRef(node, id, creation);
+
+ case OtpExternal.newRefTag:
+ final int arity = read2BE();
+ node = read_atom();
+ creation = read1() & 0x03; // 2 bits
+
+ final int[] ids = new int[arity];
+ for (int i = 0; i < arity; i++) {
+ ids[i] = read4BE();
+ }
+ ids[0] &= 0x3ffff; // first id gets truncated to 18 bits
+ return new OtpErlangRef(node, ids, creation);
+
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected ref, got " + tag);
+ }
+ }
+
+ public OtpErlangFun read_fun() throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+ if (tag == OtpExternal.funTag) {
+ final int nFreeVars = read4BE();
+ final OtpErlangPid pid = read_pid();
+ final String module = read_atom();
+ final long index = read_long();
+ final long uniq = read_long();
+ final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
+ for (int i = 0; i < nFreeVars; ++i) {
+ freeVars[i] = read_any();
+ }
+ return new OtpErlangFun(pid, module, index, uniq, freeVars);
+ } else if (tag == OtpExternal.newFunTag) {
+ final int n = read4BE();
+ final int arity = read1();
+ final byte[] md5 = new byte[16];
+ readN(md5);
+ final int index = read4BE();
+ final int nFreeVars = read4BE();
+ final String module = read_atom();
+ final long oldIndex = read_long();
+ final long uniq = read_long();
+ final OtpErlangPid pid = read_pid();
+ final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
+ for (int i = 0; i < nFreeVars; ++i) {
+ freeVars[i] = read_any();
+ }
+ return new OtpErlangFun(pid, module, arity, md5, index, oldIndex,
+ uniq, freeVars);
+ } else {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected fun, got " + tag);
+ }
+ }
+
+ public OtpErlangExternalFun read_external_fun()
+ throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+ if (tag != OtpExternal.externalFunTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected external fun, got " + tag);
+ }
+ final String module = read_atom();
+ final String function = read_atom();
+ final int arity = (int) read_long();
+ return new OtpErlangExternalFun(module, function, arity);
+ }
+
+ /**
+ * Read a string from the stream.
+ *
+ * @return the value of the string.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a string.
+ */
+ public String read_string() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] strbuf;
+ int[] intbuf;
+ tag = read1skip_version();
+ switch (tag) {
+ case OtpExternal.stringTag:
+ len = read2BE();
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ return OtpErlangString.newString(strbuf);
+ case OtpExternal.nilTag:
+ return "";
+ case OtpExternal.listTag: // List when unicode +
+ len = read4BE();
+ intbuf = new int[len];
+ for (int i = 0; i < len; i++) {
+ intbuf[i] = read_int();
+ if (! OtpErlangString.isValidCodePoint(intbuf[i])) {
+ throw new OtpErlangDecodeException
+ ("Invalid CodePoint: " + intbuf[i]);
+ }
+ }
+ read_nil();
+ return new String(intbuf, 0, intbuf.length);
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.stringTag
+ + " or " + OtpExternal.listTag + ", got " + tag);
+ }
+ }
+
+ /**
+ * Read a compressed term from the stream
+ *
+ * @return the resulting uncompressed term.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a compressed term.
+ */
+ public OtpErlangObject read_compressed() throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+
+ if (tag != OtpExternal.compressedTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected "
+ + OtpExternal.compressedTag + ", got " + tag);
+ }
+
+ final int size = read4BE();
+ final byte[] buf = new byte[size];
+ final java.util.zip.InflaterInputStream is =
+ new java.util.zip.InflaterInputStream(this);
+ try {
+ final int dsize = is.read(buf, 0, size);
+ if (dsize != size) {
+ throw new OtpErlangDecodeException("Decompression gave "
+ + dsize + " bytes, not " + size);
+ }
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+
+ final OtpInputStream ois = new OtpInputStream(buf, flags);
+ return ois.read_any();
+ }
+
+ /**
+ * Read an arbitrary Erlang term from the stream.
+ *
+ * @return the Erlang term.
+ *
+ * @exception OtpErlangDecodeException
+ * if the stream does not contain a known Erlang type at the
+ * next position.
+ */
+ public OtpErlangObject read_any() throws OtpErlangDecodeException {
+ // calls one of the above functions, depending on o
+ final int tag = peek1skip_version();
+
+ switch (tag) {
+ case OtpExternal.smallIntTag:
+ case OtpExternal.intTag:
+ case OtpExternal.smallBigTag:
+ case OtpExternal.largeBigTag:
+ return new OtpErlangLong(this);
+
+ case OtpExternal.atomTag:
+ return new OtpErlangAtom(this);
+
+ case OtpExternal.floatTag:
+ case OtpExternal.newFloatTag:
+ return new OtpErlangDouble(this);
+
+ case OtpExternal.refTag:
+ case OtpExternal.newRefTag:
+ return new OtpErlangRef(this);
+
+ case OtpExternal.portTag:
+ return new OtpErlangPort(this);
+
+ case OtpExternal.pidTag:
+ return new OtpErlangPid(this);
+
+ case OtpExternal.stringTag:
+ return new OtpErlangString(this);
+
+ case OtpExternal.listTag:
+ case OtpExternal.nilTag:
+ if ((flags & DECODE_INT_LISTS_AS_STRINGS) != 0) {
+ final int savePos = getPos();
+ try {
+ return new OtpErlangString(this);
+ } catch (final OtpErlangDecodeException e) {
+ }
+ setPos(savePos);
+ }
+ return new OtpErlangList(this);
+
+ case OtpExternal.smallTupleTag:
+ case OtpExternal.largeTupleTag:
+ return new OtpErlangTuple(this);
+
+ case OtpExternal.binTag:
+ return new OtpErlangBinary(this);
+
+ case OtpExternal.bitBinTag:
+ return new OtpErlangBitstr(this);
+
+ case OtpExternal.compressedTag:
+ return read_compressed();
+
+ case OtpExternal.newFunTag:
+ case OtpExternal.funTag:
+ return new OtpErlangFun(this);
+
+ default:
+ throw new OtpErlangDecodeException("Uknown data type: " + tag);
+ }
+ }
+}