1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2000-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 package com.ericsson.otp.erlang;
21 
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.math.BigDecimal;
25 import java.util.Arrays;
26 
27 /**
28  * Provides a stream for decoding Erlang terms from external format.
29  *
30  * <p>
31  * Note that this class is not synchronized, if you need synchronization you
32  * must provide it yourself.
33  */
34 public class OtpInputStream extends ByteArrayInputStream {
35 
36     public static int DECODE_INT_LISTS_AS_STRINGS = 1;
37 
38     private final int flags;
39 
40     /**
41      * @param buf
42      */
OtpInputStream(final byte[] buf)43     public OtpInputStream(final byte[] buf) {
44         this(buf, 0);
45     }
46 
47     /**
48      * Create a stream from a buffer containing encoded Erlang terms.
49      *
50      * @param flags
51      */
OtpInputStream(final byte[] buf, final int flags)52     public OtpInputStream(final byte[] buf, final int flags) {
53         super(buf);
54         this.flags = flags;
55     }
56 
57     /**
58      * Create a stream from a buffer containing encoded Erlang terms at the
59      * given offset and length.
60      *
61      * @param flags
62      */
OtpInputStream(final byte[] buf, final int offset, final int length, final int flags)63     public OtpInputStream(final byte[] buf, final int offset, final int length,
64             final int flags) {
65         super(buf, offset, length);
66         this.flags = flags;
67     }
68 
69     /**
70      * Get the current position in the stream.
71      *
72      * @return the current position in the stream.
73      */
getPos()74     public int getPos() {
75         return super.pos;
76     }
77 
78     /**
79      * Set the current position in the stream.
80      *
81      * @param pos
82      *            the position to move to in the stream. If pos indicates a
83      *            position beyond the end of the stream, the position is move to
84      *            the end of the stream instead. If pos is negative, the
85      *            position is moved to the beginning of the stream instead.
86      *
87      * @return the previous position in the stream.
88      */
setPos(final int pos)89     public int setPos(final int pos) {
90         final int oldpos = super.pos;
91 
92         int apos = pos;
93         if (pos > super.count) {
94             apos = super.count;
95         } else if (pos < 0) {
96             apos = 0;
97         }
98 
99         super.pos = apos;
100 
101         return oldpos;
102     }
103 
104     /**
105      * Read an array of bytes from the stream. The method reads at most
106      * buf.length bytes from the input stream.
107      *
108      * @return the number of bytes read.
109      *
110      * @exception OtpErlangDecodeException
111      *                if the next byte cannot be read.
112      */
readN(final byte[] abuf)113     public int readN(final byte[] abuf) throws OtpErlangDecodeException {
114         return this.readN(abuf, 0, abuf.length);
115     }
116 
117     /**
118      * Read an array of bytes from the stream. The method reads at most len
119      * bytes from the input stream into offset off of the buffer.
120      *
121      * @return the number of bytes read.
122      *
123      * @exception OtpErlangDecodeException
124      *                if the next byte cannot be read.
125      */
readN(final byte[] abuf, final int off, final int len)126     public int readN(final byte[] abuf, final int off, final int len)
127             throws OtpErlangDecodeException {
128         if (len == 0 && available() == 0) {
129             return 0;
130         }
131         final int i = super.read(abuf, off, len);
132         if (i < 0) {
133             throw new OtpErlangDecodeException("Cannot read from input stream");
134         }
135         return i;
136     }
137 
138     /**
139      * Alias for peek1()
140      */
peek()141     public int peek() throws OtpErlangDecodeException {
142         return peek1();
143     }
144 
145     /**
146      * Look ahead one position in the stream without consuming the byte found
147      * there.
148      *
149      * @return the next byte in the stream, as an integer.
150      *
151      * @exception OtpErlangDecodeException
152      *                if the next byte cannot be read.
153      */
peek1()154     public int peek1() throws OtpErlangDecodeException {
155         int i;
156         try {
157             i = super.buf[super.pos];
158             if (i < 0) {
159                 i += 256;
160             }
161 
162             return i;
163         } catch (final Exception e) {
164             throw new OtpErlangDecodeException("Cannot read from input stream");
165         }
166     }
167 
peek1skip_version()168     public int peek1skip_version() throws OtpErlangDecodeException {
169         int i = peek1();
170         if (i == OtpExternal.versionTag) {
171             read1();
172             i = peek1();
173         }
174         return i;
175     }
176 
177     /**
178      * Read a one byte integer from the stream.
179      *
180      * @return the byte read, as an integer.
181      *
182      * @exception OtpErlangDecodeException
183      *                if the next byte cannot be read.
184      */
read1()185     public int read1() throws OtpErlangDecodeException {
186         int i;
187         i = super.read();
188 
189         if (i < 0) {
190             throw new OtpErlangDecodeException("Cannot read from input stream");
191         }
192 
193         return i;
194     }
195 
read1skip_version()196     public int read1skip_version() throws OtpErlangDecodeException {
197         int tag = read1();
198         if (tag == OtpExternal.versionTag) {
199             tag = read1();
200         }
201         return tag;
202     }
203 
204     /**
205      * Read a two byte big endian integer from the stream.
206      *
207      * @return the bytes read, converted from big endian to an integer.
208      *
209      * @exception OtpErlangDecodeException
210      *                if the next byte cannot be read.
211      */
read2BE()212     public int read2BE() throws OtpErlangDecodeException {
213         final byte[] b = new byte[2];
214         try {
215             super.read(b);
216         } catch (final IOException e) {
217             throw new OtpErlangDecodeException("Cannot read from input stream");
218         }
219         return (b[0] << 8 & 0xff00) + (b[1] & 0xff);
220     }
221 
222     /**
223      * Read a four byte big endian integer from the stream.
224      *
225      * @return the bytes read, converted from big endian to an integer.
226      *
227      * @exception OtpErlangDecodeException
228      *                if the next byte cannot be read.
229      */
read4BE()230     public int read4BE() throws OtpErlangDecodeException {
231         final byte[] b = new byte[4];
232         try {
233             super.read(b);
234         } catch (final IOException e) {
235             throw new OtpErlangDecodeException("Cannot read from input stream");
236         }
237         return (b[0] << 24 & 0xff000000) + (b[1] << 16 & 0xff0000)
238                 + (b[2] << 8 & 0xff00) + (b[3] & 0xff);
239     }
240 
241     /**
242      * Read a eight byte big endian integer from the stream.
243      *
244      * @return the bytes read, converted from big endian to a long integer.
245      *
246      * @exception OtpErlangDecodeException
247      *                if the next byte cannot be read.
248      */
read8BE()249     public long read8BE() throws OtpErlangDecodeException {
250         long high = read4BE();
251         long low = read4BE();
252         return (high << 32) | (low & 0xffffffff);
253     }
254 
255     /**
256      * Read a two byte little endian integer from the stream.
257      *
258      * @return the bytes read, converted from little endian to an integer.
259      *
260      * @exception OtpErlangDecodeException
261      *                if the next byte cannot be read.
262      */
read2LE()263     public int read2LE() throws OtpErlangDecodeException {
264         final byte[] b = new byte[2];
265         try {
266             super.read(b);
267         } catch (final IOException e) {
268             throw new OtpErlangDecodeException("Cannot read from input stream");
269         }
270         return (b[1] << 8 & 0xff00) + (b[0] & 0xff);
271     }
272 
273     /**
274      * Read a four byte little endian integer from the stream.
275      *
276      * @return the bytes read, converted from little endian to an integer.
277      *
278      * @exception OtpErlangDecodeException
279      *                if the next byte cannot be read.
280      */
read4LE()281     public int read4LE() throws OtpErlangDecodeException {
282         final byte[] b = new byte[4];
283         try {
284             super.read(b);
285         } catch (final IOException e) {
286             throw new OtpErlangDecodeException("Cannot read from input stream");
287         }
288         return (b[3] << 24 & 0xff000000) + (b[2] << 16 & 0xff0000)
289                 + (b[1] << 8 & 0xff00) + (b[0] & 0xff);
290     }
291 
292     /**
293      * Read a little endian integer from the stream.
294      *
295      * @param n
296      *            the number of bytes to read
297      *
298      * @return the bytes read, converted from little endian to an integer.
299      *
300      * @exception OtpErlangDecodeException
301      *                if the next byte cannot be read.
302      */
readLE(final int n)303     public long readLE(final int n) throws OtpErlangDecodeException {
304         final byte[] b = new byte[n];
305         try {
306             super.read(b);
307         } catch (final IOException e) {
308             throw new OtpErlangDecodeException("Cannot read from input stream");
309         }
310         long v = 0;
311         int i = n;
312         while (i-- > 0) {
313             v = v << 8 | (long) b[i] & 0xff;
314         }
315         return v;
316     }
317 
318     /**
319      * Read a bigendian integer from the stream.
320      *
321      * @param n
322      *            the number of bytes to read
323      *
324      * @return the bytes read, converted from big endian to an integer.
325      *
326      * @exception OtpErlangDecodeException
327      *                if the next byte cannot be read.
328      */
readBE(final int n)329     public long readBE(final int n) throws OtpErlangDecodeException {
330         final byte[] b = new byte[n];
331         try {
332             super.read(b);
333         } catch (final IOException e) {
334             throw new OtpErlangDecodeException("Cannot read from input stream");
335         }
336         long v = 0;
337         for (int i = 0; i < n; i++) {
338             v = v << 8 | (long) b[i] & 0xff;
339         }
340         return v;
341     }
342 
343     /**
344      * Read an Erlang atom from the stream and interpret the value as a boolean.
345      *
346      * @return true if the atom at the current position in the stream contains
347      *         the value 'true' (ignoring case), false otherwise.
348      *
349      * @exception OtpErlangDecodeException
350      *                if the next term in the stream is not an atom.
351      */
read_boolean()352     public boolean read_boolean() throws OtpErlangDecodeException {
353         return Boolean.valueOf(read_atom()).booleanValue();
354     }
355 
356     /**
357      * Read an Erlang atom from the stream.
358      *
359      * @return a String containing the value of the atom.
360      *
361      * @exception OtpErlangDecodeException
362      *                if the next term in the stream is not an atom.
363      */
364     @SuppressWarnings("fallthrough")
read_atom()365     public String read_atom() throws OtpErlangDecodeException {
366         int tag;
367         int len = -1;
368         byte[] strbuf;
369         String atom;
370 
371         tag = read1skip_version();
372 
373         switch (tag) {
374 
375         case OtpExternal.atomTag:
376             len = read2BE();
377             strbuf = new byte[len];
378             this.readN(strbuf);
379             try {
380                 atom = new String(strbuf, "ISO-8859-1");
381             } catch (final java.io.UnsupportedEncodingException e) {
382                 throw new OtpErlangDecodeException(
383                         "Failed to decode ISO-8859-1 atom");
384             }
385             if (atom.length() > OtpExternal.maxAtomLength) {
386                 /*
387                  * Throwing an exception would be better I think, but truncation
388                  * seems to be the way it has been done in other parts of OTP...
389                  */
390                 atom = atom.substring(0, OtpExternal.maxAtomLength);
391             }
392             break;
393 
394         case OtpExternal.smallAtomUtf8Tag:
395             len = read1();
396             // fall-through
397         case OtpExternal.atomUtf8Tag:
398             if (len < 0) {
399                 len = read2BE();
400             }
401             strbuf = new byte[len];
402             this.readN(strbuf);
403             try {
404                 atom = new String(strbuf, "UTF-8");
405             } catch (final java.io.UnsupportedEncodingException e) {
406                 throw new OtpErlangDecodeException(
407                         "Failed to decode UTF-8 atom");
408             }
409             if (atom.codePointCount(0, atom.length()) > OtpExternal.maxAtomLength) {
410                 /*
411                  * Throwing an exception would be better I think, but truncation
412                  * seems to be the way it has been done in other parts of OTP...
413                  */
414                 final int[] cps = OtpErlangString.stringToCodePoints(atom);
415                 atom = new String(cps, 0, OtpExternal.maxAtomLength);
416             }
417             break;
418 
419         default:
420             throw new OtpErlangDecodeException(
421                     "wrong tag encountered, expected " + OtpExternal.atomTag
422                             + ", or " + OtpExternal.atomUtf8Tag + ", got "
423                             + tag);
424         }
425 
426         return atom;
427     }
428 
429     /**
430      * Read an Erlang binary from the stream.
431      *
432      * @return a byte array containing the value of the binary.
433      *
434      * @exception OtpErlangDecodeException
435      *                if the next term in the stream is not a binary.
436      */
read_binary()437     public byte[] read_binary() throws OtpErlangDecodeException {
438         int tag;
439         int len;
440         byte[] bin;
441 
442         tag = read1skip_version();
443 
444         if (tag != OtpExternal.binTag) {
445             throw new OtpErlangDecodeException(
446                     "Wrong tag encountered, expected " + OtpExternal.binTag
447                             + ", got " + tag);
448         }
449 
450         len = read4BE();
451 
452         bin = new byte[len];
453         this.readN(bin);
454 
455         return bin;
456     }
457 
458     /**
459      * Read an Erlang bitstr from the stream.
460      *
461      * @param pad_bits
462      *            an int array whose first element will be set to the number of
463      *            pad bits in the last byte.
464      *
465      * @return a byte array containing the value of the bitstr.
466      *
467      * @exception OtpErlangDecodeException
468      *                if the next term in the stream is not a bitstr.
469      */
read_bitstr(final int pad_bits[])470     public byte[] read_bitstr(final int pad_bits[])
471             throws OtpErlangDecodeException {
472         int tag;
473         int len;
474         byte[] bin;
475 
476         tag = read1skip_version();
477 
478         if (tag != OtpExternal.bitBinTag) {
479             throw new OtpErlangDecodeException(
480                     "Wrong tag encountered, expected " + OtpExternal.bitBinTag
481                             + ", got " + tag);
482         }
483 
484         len = read4BE();
485         bin = new byte[len];
486         final int tail_bits = read1();
487         if (tail_bits < 0 || 7 < tail_bits) {
488             throw new OtpErlangDecodeException(
489                     "Wrong tail bit count in bitstr: " + tail_bits);
490         }
491         if (len == 0 && tail_bits != 0) {
492             throw new OtpErlangDecodeException(
493                     "Length 0 on bitstr with tail bit count: " + tail_bits);
494         }
495         this.readN(bin);
496 
497         pad_bits[0] = 8 - tail_bits;
498         return bin;
499     }
500 
501     /**
502      * Read an Erlang float from the stream.
503      *
504      * @return the float value.
505      *
506      * @exception OtpErlangDecodeException
507      *                if the next term in the stream is not a float.
508      */
read_float()509     public float read_float() throws OtpErlangDecodeException {
510         final double d = read_double();
511         return (float) d;
512     }
513 
514     /**
515      * Read an Erlang float from the stream.
516      *
517      * @return the float value, as a double.
518      *
519      * @exception OtpErlangDecodeException
520      *                if the next term in the stream is not a float.
521      */
read_double()522     public double read_double() throws OtpErlangDecodeException {
523         int tag;
524 
525         // parse the stream
526         tag = read1skip_version();
527 
528         switch (tag) {
529         case OtpExternal.newFloatTag: {
530             return Double.longBitsToDouble(readBE(8));
531         }
532         case OtpExternal.floatTag: {
533             BigDecimal val;
534             int epos;
535             int exp;
536             final byte[] strbuf = new byte[31];
537             String str;
538 
539             // get the string
540             this.readN(strbuf);
541             str = OtpErlangString.newString(strbuf);
542 
543             // find the exponent prefix 'e' in the string
544             epos = str.indexOf('e', 0);
545 
546             if (epos < 0) {
547                 throw new OtpErlangDecodeException("Invalid float format: '"
548                         + str + "'");
549             }
550 
551             // remove the sign from the exponent, if positive
552             String estr = str.substring(epos + 1).trim();
553 
554             if (estr.substring(0, 1).equals("+")) {
555                 estr = estr.substring(1);
556             }
557 
558             // now put the mantissa and exponent together
559             exp = Integer.valueOf(estr).intValue();
560             val = new BigDecimal(str.substring(0, epos)).movePointRight(exp);
561 
562             return val.doubleValue();
563         }
564         default:
565             throw new OtpErlangDecodeException(
566                     "Wrong tag encountered, expected "
567                             + OtpExternal.newFloatTag + ", got " + tag);
568         }
569     }
570 
571     /**
572      * Read one byte from the stream.
573      *
574      * @return the byte read.
575      *
576      * @exception OtpErlangDecodeException
577      *                if the next byte cannot be read.
578      */
read_byte()579     public byte read_byte() throws OtpErlangDecodeException {
580         final long l = this.read_long(false);
581         final byte i = (byte) l;
582 
583         if (l != i) {
584             throw new OtpErlangDecodeException("Value does not fit in byte: "
585                     + l);
586         }
587 
588         return i;
589     }
590 
591     /**
592      * Read a character from the stream.
593      *
594      * @return the character value.
595      *
596      * @exception OtpErlangDecodeException
597      *                if the next term in the stream is not an integer that can
598      *                be represented as a char.
599      */
read_char()600     public char read_char() throws OtpErlangDecodeException {
601         final long l = this.read_long(true);
602         final char i = (char) l;
603 
604         if (l != (i & 0xffffL)) {
605             throw new OtpErlangDecodeException("Value does not fit in char: "
606                     + l);
607         }
608 
609         return i;
610     }
611 
612     /**
613      * Read an unsigned integer from the stream.
614      *
615      * @return the integer value.
616      *
617      * @exception OtpErlangDecodeException
618      *                if the next term in the stream cannot be represented as a
619      *                positive integer.
620      */
read_uint()621     public int read_uint() throws OtpErlangDecodeException {
622         final long l = this.read_long(true);
623         final int i = (int) l;
624 
625         if (l != (i & 0xFFFFffffL)) {
626             throw new OtpErlangDecodeException("Value does not fit in uint: "
627                     + l);
628         }
629 
630         return i;
631     }
632 
633     /**
634      * Read an integer from the stream.
635      *
636      * @return the integer value.
637      *
638      * @exception OtpErlangDecodeException
639      *                if the next term in the stream cannot be represented as
640      *                an integer.
641      */
read_int()642     public int read_int() throws OtpErlangDecodeException {
643         final long l = this.read_long(false);
644         final int i = (int) l;
645 
646         if (l != i) {
647             throw new OtpErlangDecodeException("Value does not fit in int: "
648                     + l);
649         }
650 
651         return i;
652     }
653 
654     /**
655      * Read an unsigned short from the stream.
656      *
657      * @return the short value.
658      *
659      * @exception OtpErlangDecodeException
660      *                if the next term in the stream cannot be represented as a
661      *                positive short.
662      */
read_ushort()663     public short read_ushort() throws OtpErlangDecodeException {
664         final long l = this.read_long(true);
665         final short i = (short) l;
666 
667         if (l != (i & 0xffffL)) {
668             throw new OtpErlangDecodeException("Value does not fit in ushort: "
669                     + l);
670         }
671 
672         return i;
673     }
674 
675     /**
676      * Read a short from the stream.
677      *
678      * @return the short value.
679      *
680      * @exception OtpErlangDecodeException
681      *                if the next term in the stream cannot be represented as a
682      *                short.
683      */
read_short()684     public short read_short() throws OtpErlangDecodeException {
685         final long l = this.read_long(false);
686         final short i = (short) l;
687 
688         if (l != i) {
689             throw new OtpErlangDecodeException("Value does not fit in short: "
690                     + l);
691         }
692 
693         return i;
694     }
695 
696     /**
697      * Read an unsigned long from the stream.
698      *
699      * @return the long value.
700      *
701      * @exception OtpErlangDecodeException
702      *                if the next term in the stream cannot be represented as a
703      *                positive long.
704      */
read_ulong()705     public long read_ulong() throws OtpErlangDecodeException {
706         return this.read_long(true);
707     }
708 
709     /**
710      * Read a long from the stream.
711      *
712      * @return the long value.
713      *
714      * @exception OtpErlangDecodeException
715      *                if the next term in the stream cannot be represented as a
716      *                long.
717      */
read_long()718     public long read_long() throws OtpErlangDecodeException {
719         return this.read_long(false);
720     }
721 
read_long(final boolean unsigned)722     public long read_long(final boolean unsigned)
723             throws OtpErlangDecodeException {
724         final byte[] b = read_integer_byte_array();
725         return OtpInputStream.byte_array_to_long(b, unsigned);
726     }
727 
728     /**
729      * Read an integer from the stream.
730      *
731      * @return the value as a big endian 2's complement byte array.
732      *
733      * @exception OtpErlangDecodeException
734      *                if the next term in the stream is not an integer.
735      */
read_integer_byte_array()736     public byte[] read_integer_byte_array() throws OtpErlangDecodeException {
737         int tag;
738         byte[] nb;
739 
740         tag = read1skip_version();
741 
742         switch (tag) {
743         case OtpExternal.smallIntTag:
744             nb = new byte[2];
745             nb[0] = 0;
746             nb[1] = (byte) read1();
747             break;
748 
749         case OtpExternal.intTag:
750             nb = new byte[4];
751             if (this.readN(nb) != 4) { // Big endian
752                 throw new OtpErlangDecodeException(
753                         "Cannot read from intput stream");
754             }
755             break;
756 
757         case OtpExternal.smallBigTag:
758         case OtpExternal.largeBigTag:
759             int arity;
760             int sign;
761             if (tag == OtpExternal.smallBigTag) {
762                 arity = read1();
763                 sign = read1();
764             } else {
765                 arity = read4BE();
766                 sign = read1();
767                 if (arity + 1 < 0) {
768                     throw new OtpErlangDecodeException(
769                             "Value of largeBig does not fit in BigInteger, arity "
770                                     + arity + " sign " + sign);
771                 }
772             }
773             nb = new byte[arity + 1];
774             // Value is read as little endian. The big end is augumented
775             // with one zero byte to make the value 2's complement positive.
776             if (this.readN(nb, 0, arity) != arity) {
777                 throw new OtpErlangDecodeException(
778                         "Cannot read from intput stream");
779             }
780             // Reverse the array to make it big endian.
781             for (int i = 0, j = nb.length; i < j--; i++) {
782                 // Swap [i] with [j]
783                 final byte b = nb[i];
784                 nb[i] = nb[j];
785                 nb[j] = b;
786             }
787             if (sign != 0) {
788                 // 2's complement negate the big endian value in the array
789                 int c = 1; // Carry
790                 for (int j = nb.length; j-- > 0;) {
791                     c = (~nb[j] & 0xFF) + c;
792                     nb[j] = (byte) c;
793                     c >>= 8;
794                 }
795             }
796             break;
797 
798         default:
799             throw new OtpErlangDecodeException("Not valid integer tag: " + tag);
800         }
801 
802         return nb;
803     }
804 
byte_array_to_long(final byte[] b, final boolean unsigned)805     public static long byte_array_to_long(final byte[] b, final boolean unsigned)
806             throws OtpErlangDecodeException {
807         long v;
808         switch (b.length) {
809         case 0:
810             v = 0;
811             break;
812         case 2:
813             v = ((b[0] & 0xFF) << 8) + (b[1] & 0xFF);
814             v = (short) v; // Sign extend
815             if (v < 0 && unsigned) {
816                 throw new OtpErlangDecodeException("Value not unsigned: " + v);
817             }
818             break;
819         case 4:
820             v = ((b[0] & 0xFF) << 24) + ((b[1] & 0xFF) << 16)
821                     + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
822             v = (int) v; // Sign extend
823             if (v < 0 && unsigned) {
824                 throw new OtpErlangDecodeException("Value not unsigned: " + v);
825             }
826             break;
827         default:
828             int i = 0;
829             final byte c = b[i];
830             // Skip non-essential leading bytes
831             if (unsigned) {
832                 if (c < 0) {
833                     throw new OtpErlangDecodeException("Value not unsigned: "
834                             + Arrays.toString(b));
835                 }
836                 while (b[i] == 0) {
837                     i++; // Skip leading zero sign bytes
838                 }
839             } else {
840                 if (c == 0 || c == -1) { // Leading sign byte
841                     i = 1;
842                     // Skip all leading sign bytes
843                     while (i < b.length && b[i] == c) {
844                         i++;
845                     }
846                     if (i < b.length) {
847                         // Check first non-sign byte to see if its sign
848                         // matches the whole number's sign. If not one more
849                         // byte is needed to represent the value.
850                         if (((c ^ b[i]) & 0x80) != 0) {
851                             i--;
852                         }
853                     }
854                 }
855             }
856             if (b.length - i > 8) {
857                 // More than 64 bits of value
858                 throw new OtpErlangDecodeException(
859                         "Value does not fit in long: " + Arrays.toString(b));
860             }
861             // Convert the necessary bytes
862             for (v = c < 0 ? -1 : 0; i < b.length; i++) {
863                 v = v << 8 | b[i] & 0xFF;
864             }
865         }
866         return v;
867     }
868 
869     /**
870      * Read a list header from the stream.
871      *
872      * @return the arity of the list.
873      *
874      * @exception OtpErlangDecodeException
875      *                if the next term in the stream is not a list.
876      */
read_list_head()877     public int read_list_head() throws OtpErlangDecodeException {
878         int arity = 0;
879         final int tag = read1skip_version();
880 
881         switch (tag) {
882         case OtpExternal.nilTag:
883             arity = 0;
884             break;
885 
886         case OtpExternal.stringTag:
887             arity = read2BE();
888             break;
889 
890         case OtpExternal.listTag:
891             arity = read4BE();
892             break;
893 
894         default:
895             throw new OtpErlangDecodeException("Not valid list tag: " + tag);
896         }
897 
898         return arity;
899     }
900 
901     /**
902      * Read a tuple header from the stream.
903      *
904      * @return the arity of the tuple.
905      *
906      * @exception OtpErlangDecodeException
907      *                if the next term in the stream is not a tuple.
908      */
read_tuple_head()909     public int read_tuple_head() throws OtpErlangDecodeException {
910         int arity = 0;
911         final int tag = read1skip_version();
912 
913         // decode the tuple header and get arity
914         switch (tag) {
915         case OtpExternal.smallTupleTag:
916             arity = read1();
917             break;
918 
919         case OtpExternal.largeTupleTag:
920             arity = read4BE();
921             break;
922 
923         default:
924             throw new OtpErlangDecodeException("Not valid tuple tag: " + tag);
925         }
926 
927         return arity;
928     }
929 
930     /**
931      * Read an empty list from the stream.
932      *
933      * @return zero (the arity of the list).
934      *
935      * @exception OtpErlangDecodeException
936      *                if the next term in the stream is not an empty list.
937      */
read_nil()938     public int read_nil() throws OtpErlangDecodeException {
939         int arity = 0;
940         final int tag = read1skip_version();
941 
942         switch (tag) {
943         case OtpExternal.nilTag:
944             arity = 0;
945             break;
946 
947         default:
948             throw new OtpErlangDecodeException("Not valid nil tag: " + tag);
949         }
950 
951         return arity;
952     }
953 
954     /**
955      * Read an Erlang PID from the stream.
956      *
957      * @return the value of the PID.
958      *
959      * @exception OtpErlangDecodeException
960      *                if the next term in the stream is not an Erlang PID.
961      */
read_pid()962     public OtpErlangPid read_pid() throws OtpErlangDecodeException {
963         String node;
964         int id;
965         int serial;
966         int creation;
967         int tag;
968 
969         tag = read1skip_version();
970 
971         if (tag != OtpExternal.pidTag &&
972 	    tag != OtpExternal.newPidTag) {
973             throw new OtpErlangDecodeException(
974                     "Wrong tag encountered, expected " + OtpExternal.pidTag
975 		    + " or " + OtpExternal.newPidTag
976                             + ", got " + tag);
977         }
978 
979         node = read_atom();
980         id = read4BE();
981         serial = read4BE();
982 	if (tag == OtpExternal.pidTag)
983 	    creation = read1();
984 	else
985 	    creation = read4BE();
986 
987         return new OtpErlangPid(tag, node, id, serial, creation);
988     }
989 
990     /**
991      * Read an Erlang port from the stream.
992      *
993      * @return the value of the port.
994      *
995      * @exception OtpErlangDecodeException
996      *                if the next term in the stream is not an Erlang port.
997      */
read_port()998     public OtpErlangPort read_port() throws OtpErlangDecodeException {
999         String node;
1000         long id;
1001         int creation;
1002         int tag;
1003 
1004         tag = read1skip_version();
1005 
1006         if (tag != OtpExternal.portTag &&
1007 	    tag != OtpExternal.newPortTag &&
1008 	    tag != OtpExternal.v4PortTag) {
1009             throw new OtpErlangDecodeException(
1010                     "Wrong tag encountered, expected " + OtpExternal.portTag
1011 		    + ", " + OtpExternal.newPortTag + ", or "
1012                      + OtpExternal.v4PortTag + ", got " + tag);
1013         }
1014 
1015         node = read_atom();
1016         if (tag == OtpExternal.v4PortTag) {
1017             id = read8BE();
1018 	    creation = read4BE();
1019         }
1020         else if (tag == OtpExternal.newPortTag) {
1021             id = (long) read4BE();
1022 	    creation = read4BE();
1023         }
1024         else {
1025             id = read4BE();
1026 	    creation = read1();
1027         }
1028 
1029         return new OtpErlangPort(tag, node, id, creation);
1030     }
1031 
1032     /**
1033      * Read an Erlang reference from the stream.
1034      *
1035      * @return the value of the reference
1036      *
1037      * @exception OtpErlangDecodeException
1038      *                if the next term in the stream is not an Erlang reference.
1039      */
read_ref()1040     public OtpErlangRef read_ref() throws OtpErlangDecodeException {
1041         String node;
1042         int id;
1043         int creation;
1044         int tag;
1045 
1046         tag = read1skip_version();
1047 
1048         switch (tag) {
1049         case OtpExternal.refTag:
1050             node = read_atom();
1051             id = read4BE() & 0x3ffff; // 18 bits
1052             creation = read1() & 0x03; // 2 bits
1053             return new OtpErlangRef(node, id, creation);
1054 
1055         case OtpExternal.newRefTag:
1056         case OtpExternal.newerRefTag:
1057             final int arity = read2BE();
1058             if (arity > 5) {
1059 		throw new OtpErlangDecodeException(
1060 		    "Ref arity " + arity + " too large ");
1061 	    }
1062             node = read_atom();
1063 	    if (tag == OtpExternal.newRefTag)
1064 		creation = read1();
1065 	    else
1066 		creation = read4BE();
1067 
1068             final int[] ids = new int[arity];
1069             for (int i = 0; i < arity; i++) {
1070                 ids[i] = read4BE();
1071             }
1072             return new OtpErlangRef(tag, node, ids, creation);
1073 
1074         default:
1075             throw new OtpErlangDecodeException(
1076                     "Wrong tag encountered, expected ref, got " + tag);
1077         }
1078     }
1079 
read_fun()1080     public OtpErlangFun read_fun() throws OtpErlangDecodeException {
1081         final int tag = read1skip_version();
1082         if (tag == OtpExternal.funTag) {
1083             final int nFreeVars = read4BE();
1084             final OtpErlangPid pid = read_pid();
1085             final String module = read_atom();
1086             final long index = read_long();
1087             final long uniq = read_long();
1088             final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
1089             for (int i = 0; i < nFreeVars; ++i) {
1090                 freeVars[i] = read_any();
1091             }
1092             return new OtpErlangFun(pid, module, index, uniq, freeVars);
1093         } else if (tag == OtpExternal.newFunTag) {
1094             read4BE();
1095             final int arity = read1();
1096             final byte[] md5 = new byte[16];
1097             readN(md5);
1098             final int index = read4BE();
1099             final int nFreeVars = read4BE();
1100             final String module = read_atom();
1101             final long oldIndex = read_long();
1102             final long uniq = read_long();
1103             final OtpErlangPid pid = read_pid();
1104             final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
1105             for (int i = 0; i < nFreeVars; ++i) {
1106                 freeVars[i] = read_any();
1107             }
1108             return new OtpErlangFun(pid, module, arity, md5, index, oldIndex,
1109                     uniq, freeVars);
1110         } else {
1111             throw new OtpErlangDecodeException(
1112                     "Wrong tag encountered, expected fun, got " + tag);
1113         }
1114     }
1115 
read_external_fun()1116     public OtpErlangExternalFun read_external_fun()
1117             throws OtpErlangDecodeException {
1118         final int tag = read1skip_version();
1119         if (tag != OtpExternal.externalFunTag) {
1120             throw new OtpErlangDecodeException(
1121                     "Wrong tag encountered, expected external fun, got " + tag);
1122         }
1123         final String module = read_atom();
1124         final String function = read_atom();
1125         final int arity = (int) read_long();
1126         return new OtpErlangExternalFun(module, function, arity);
1127     }
1128 
1129     /**
1130      * Read a string from the stream.
1131      *
1132      * @return the value of the string.
1133      *
1134      * @exception OtpErlangDecodeException
1135      *                if the next term in the stream is not a string.
1136      */
read_string()1137     public String read_string() throws OtpErlangDecodeException {
1138         int tag;
1139         int len;
1140         byte[] strbuf;
1141         int[] intbuf;
1142         tag = read1skip_version();
1143         switch (tag) {
1144         case OtpExternal.stringTag:
1145             len = read2BE();
1146             strbuf = new byte[len];
1147             this.readN(strbuf);
1148             return OtpErlangString.newString(strbuf);
1149         case OtpExternal.nilTag:
1150             return "";
1151         case OtpExternal.listTag: // List when unicode +
1152             len = read4BE();
1153             intbuf = new int[len];
1154             for (int i = 0; i < len; i++) {
1155                 intbuf[i] = read_int();
1156                 if (!OtpErlangString.isValidCodePoint(intbuf[i])) {
1157                     throw new OtpErlangDecodeException("Invalid CodePoint: "
1158                             + intbuf[i]);
1159                 }
1160             }
1161             read_nil();
1162             return new String(intbuf, 0, intbuf.length);
1163         default:
1164             throw new OtpErlangDecodeException(
1165                     "Wrong tag encountered, expected " + OtpExternal.stringTag
1166                             + " or " + OtpExternal.listTag + ", got " + tag);
1167         }
1168     }
1169 
1170     /**
1171      * Read a compressed term from the stream
1172      *
1173      * @return the resulting uncompressed term.
1174      *
1175      * @exception OtpErlangDecodeException
1176      *                if the next term in the stream is not a compressed term.
1177      */
read_compressed()1178     public OtpErlangObject read_compressed() throws OtpErlangDecodeException {
1179         final int tag = read1skip_version();
1180 
1181         if (tag != OtpExternal.compressedTag) {
1182             throw new OtpErlangDecodeException(
1183                     "Wrong tag encountered, expected "
1184                             + OtpExternal.compressedTag + ", got " + tag);
1185         }
1186 
1187         final int size = read4BE();
1188         final byte[] abuf = new byte[size];
1189         final java.util.zip.InflaterInputStream is = new java.util.zip.InflaterInputStream(
1190                 this, new java.util.zip.Inflater(), size);
1191         int curPos = 0;
1192         try {
1193             int curRead;
1194             while (curPos < size
1195                     && (curRead = is.read(abuf, curPos, size - curPos)) != -1) {
1196                 curPos += curRead;
1197             }
1198             if (curPos != size) {
1199                 throw new OtpErlangDecodeException("Decompression gave "
1200                         + curPos + " bytes, not " + size);
1201             }
1202         } catch (final IOException e) {
1203             throw new OtpErlangDecodeException("Cannot read from input stream");
1204         }
1205 
1206         @SuppressWarnings("resource")
1207         final OtpInputStream ois = new OtpInputStream(abuf, flags);
1208         return ois.read_any();
1209     }
1210 
1211     /**
1212      * Read an arbitrary Erlang term from the stream.
1213      *
1214      * @return the Erlang term.
1215      *
1216      * @exception OtpErlangDecodeException
1217      *                if the stream does not contain a known Erlang type at the
1218      *                next position.
1219      */
read_any()1220     public OtpErlangObject read_any() throws OtpErlangDecodeException {
1221         // calls one of the above functions, depending on o
1222         final int tag = peek1skip_version();
1223 
1224         switch (tag) {
1225         case OtpExternal.smallIntTag:
1226         case OtpExternal.intTag:
1227         case OtpExternal.smallBigTag:
1228         case OtpExternal.largeBigTag:
1229             return new OtpErlangLong(this);
1230 
1231         case OtpExternal.atomTag:
1232         case OtpExternal.smallAtomUtf8Tag:
1233         case OtpExternal.atomUtf8Tag:
1234             return new OtpErlangAtom(this);
1235 
1236         case OtpExternal.floatTag:
1237         case OtpExternal.newFloatTag:
1238             return new OtpErlangDouble(this);
1239 
1240         case OtpExternal.refTag:
1241         case OtpExternal.newRefTag:
1242         case OtpExternal.newerRefTag:
1243             return new OtpErlangRef(this);
1244 
1245         case OtpExternal.mapTag:
1246             return new OtpErlangMap(this);
1247 
1248         case OtpExternal.portTag:
1249         case OtpExternal.newPortTag:
1250         case OtpExternal.v4PortTag:
1251             return new OtpErlangPort(this);
1252 
1253         case OtpExternal.pidTag:
1254         case OtpExternal.newPidTag:
1255             return new OtpErlangPid(this);
1256 
1257         case OtpExternal.stringTag:
1258             return new OtpErlangString(this);
1259 
1260         case OtpExternal.listTag:
1261         case OtpExternal.nilTag:
1262             if ((flags & DECODE_INT_LISTS_AS_STRINGS) != 0) {
1263                 final int savePos = getPos();
1264                 try {
1265                     return new OtpErlangString(this);
1266                 } catch (final OtpErlangDecodeException e) {
1267                 }
1268                 setPos(savePos);
1269             }
1270             return new OtpErlangList(this);
1271 
1272         case OtpExternal.smallTupleTag:
1273         case OtpExternal.largeTupleTag:
1274             return new OtpErlangTuple(this);
1275 
1276         case OtpExternal.binTag:
1277             return new OtpErlangBinary(this);
1278 
1279         case OtpExternal.bitBinTag:
1280             return new OtpErlangBitstr(this);
1281 
1282         case OtpExternal.compressedTag:
1283             return read_compressed();
1284 
1285         case OtpExternal.newFunTag:
1286         case OtpExternal.funTag:
1287             return new OtpErlangFun(this);
1288 
1289 	case OtpExternal.externalFunTag:
1290 	    return new OtpErlangExternalFun(this);
1291 
1292         default:
1293             throw new OtpErlangDecodeException("Unknown data type: " + tag);
1294         }
1295     }
1296 
read_map_head()1297     public int read_map_head() throws OtpErlangDecodeException {
1298         int arity = 0;
1299         final int tag = read1skip_version();
1300 
1301         // decode the map header and get arity
1302         switch (tag) {
1303         case OtpExternal.mapTag:
1304             arity = read4BE();
1305             break;
1306 
1307         default:
1308             throw new OtpErlangDecodeException("Not valid map tag: " + tag);
1309         }
1310 
1311         return arity;
1312     }
1313 }
1314