Package com.ericsson.otp.erlang

Source Code of com.ericsson.otp.erlang.OtpInputStream

/*
* %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);
  }
    }
}
TOP

Related Classes of com.ericsson.otp.erlang.OtpInputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.