1 /* 2 * %CopyrightBegin% 3 * 4 * Copyright Ericsson AB 2000-2017. 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.OutputStream; 23 import java.io.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.io.OutputStream; 26 import java.io.UnsupportedEncodingException; 27 import java.math.BigDecimal; 28 import java.math.BigInteger; 29 import java.text.DecimalFormat; 30 import java.util.zip.Deflater; 31 32 /** 33 * Provides a stream for encoding Erlang terms to external format, for 34 * transmission or storage. 35 * 36 * <p> 37 * Note that this class is not synchronized, if you need synchronization you 38 * must provide it yourself. 39 * 40 */ 41 public class OtpOutputStream extends ByteArrayOutputStream { 42 /** The default initial size of the stream. * */ 43 public static final int defaultInitialSize = 2048; 44 45 /** 46 * The default increment used when growing the stream (increment at least 47 * this much). * 48 */ 49 public static final int defaultIncrement = 2048; 50 51 // static formats, used to encode floats and doubles 52 @SuppressWarnings("unused") 53 private static final DecimalFormat eform = new DecimalFormat("e+00;e-00"); 54 @SuppressWarnings("unused") 55 private static final BigDecimal ten = new BigDecimal(10.0); 56 @SuppressWarnings("unused") 57 private static final BigDecimal one = new BigDecimal(1.0); 58 59 private int fixedSize = Integer.MAX_VALUE; 60 61 /** 62 * Create a stream with the default initial size (2048 bytes). 63 */ OtpOutputStream()64 public OtpOutputStream() { 65 this(defaultInitialSize); 66 } 67 68 /** 69 * Create a stream with the specified initial size. 70 */ OtpOutputStream(final int size)71 public OtpOutputStream(final int size) { 72 super(size); 73 } 74 75 /** 76 * Create a stream containing the encoded version of the given Erlang term. 77 */ OtpOutputStream(final OtpErlangObject o)78 public OtpOutputStream(final OtpErlangObject o) { 79 this(); 80 write_any(o); 81 } 82 83 // package scope 84 /* 85 * Get the contents of the output stream as an input stream instead. This is 86 * used internally in {@link OtpCconnection} for tracing outgoing packages. 87 * 88 * @param offset where in the output stream to read data from when creating 89 * the input stream. The offset is necessary because header contents start 5 90 * bytes into the header buffer, whereas payload contents start at the 91 * beginning 92 * 93 * @return an input stream containing the same raw data. 94 */ getOtpInputStream(final int offset)95 OtpInputStream getOtpInputStream(final int offset) { 96 return new OtpInputStream(super.buf, offset, super.count - offset, 0); 97 } 98 99 /** 100 * Get the current position in the stream. 101 * 102 * @return the current position in the stream. 103 */ getPos()104 public int getPos() { 105 return super.count; 106 } 107 108 /** 109 * Trims the capacity of this <tt>OtpOutputStream</tt> instance to be the 110 * buffer's current size. An application can use this operation to minimize 111 * the storage of an <tt>OtpOutputStream</tt> instance. 112 */ trimToSize()113 public void trimToSize() { 114 resize(super.count); 115 } 116 resize(final int size)117 private void resize(final int size) { 118 if (size < super.buf.length) { 119 final byte[] tmp = new byte[size]; 120 System.arraycopy(super.buf, 0, tmp, 0, size); 121 super.buf = tmp; 122 } else if (size > super.buf.length) { 123 ensureCapacity(size); 124 } 125 } 126 127 /** 128 * Increases the capacity of this <tt>OtpOutputStream</tt> instance, if 129 * necessary, to ensure that it can hold at least the number of elements 130 * specified by the minimum capacity argument. 131 * 132 * @param minCapacity 133 * the desired minimum capacity 134 */ ensureCapacity(final int minCapacity)135 public void ensureCapacity(final int minCapacity) { 136 if (minCapacity > fixedSize) { 137 throw new IllegalArgumentException( 138 "Trying to increase fixed-size buffer"); 139 } 140 final int oldCapacity = super.buf.length; 141 if (minCapacity > oldCapacity) { 142 int newCapacity = oldCapacity * 3 / 2 + 1; 143 if (newCapacity < oldCapacity + defaultIncrement) { 144 newCapacity = oldCapacity + defaultIncrement; 145 } 146 if (newCapacity < minCapacity) { 147 newCapacity = minCapacity; 148 } 149 newCapacity = Math.min(fixedSize, newCapacity); 150 // minCapacity is usually close to size, so this is a win: 151 final byte[] tmp = new byte[newCapacity]; 152 System.arraycopy(super.buf, 0, tmp, 0, super.count); 153 super.buf = tmp; 154 } 155 } 156 157 /** 158 * Write one byte to the stream. 159 * 160 * @param b 161 * the byte to write. 162 * 163 */ write(final byte b)164 public void write(final byte b) { 165 ensureCapacity(super.count + 1); 166 super.buf[super.count++] = b; 167 } 168 169 /* 170 * (non-Javadoc) 171 * 172 * @see java.io.ByteArrayOutputStream#write(byte[]) 173 */ 174 @Override write(final byte[] abuf)175 public void write(final byte[] abuf) { 176 // don't assume that super.write(byte[]) calls write(buf, 0, buf.length) 177 write(abuf, 0, abuf.length); 178 } 179 180 /* 181 * (non-Javadoc) 182 * 183 * @see java.io.ByteArrayOutputStream#write(int) 184 */ 185 @Override write(final int b)186 public synchronized void write(final int b) { 187 ensureCapacity(super.count + 1); 188 super.buf[super.count] = (byte) b; 189 count += 1; 190 } 191 192 /* 193 * (non-Javadoc) 194 * 195 * @see java.io.ByteArrayOutputStream#write(byte[], int, int) 196 */ 197 @Override write(final byte[] b, final int off, final int len)198 public synchronized void write(final byte[] b, final int off, final int len) { 199 if (off < 0 || off > b.length || len < 0 || off + len - b.length > 0) { 200 throw new IndexOutOfBoundsException(); 201 } 202 ensureCapacity(super.count + len); 203 System.arraycopy(b, off, super.buf, super.count, len); 204 super.count += len; 205 } 206 207 @Override writeTo(OutputStream out)208 public synchronized void writeTo(OutputStream out) throws IOException { 209 super.writeTo(out); 210 } 211 writeToAndFlush(OutputStream out)212 public synchronized void writeToAndFlush(OutputStream out) throws IOException { 213 super.writeTo(out); 214 out.flush(); 215 } 216 217 /** 218 * Write the low byte of a value to the stream. 219 * 220 * @param n 221 * the value to use. 222 * 223 */ write1(final long n)224 public void write1(final long n) { 225 write((byte) (n & 0xff)); 226 } 227 228 /** 229 * Write an array of bytes to the stream. 230 * 231 * @param bytes 232 * the array of bytes to write. 233 * 234 */ writeN(final byte[] bytes)235 public void writeN(final byte[] bytes) { 236 write(bytes); 237 } 238 239 /** 240 * Get the current capacity of the stream. As bytes are added the capacity 241 * of the stream is increased automatically, however this method returns the 242 * current size. 243 * 244 * @return the size of the internal buffer used by the stream. 245 */ length()246 public int length() { 247 return super.buf.length; 248 } 249 250 /** 251 * Get the number of bytes in the stream. 252 * 253 * @return the number of bytes in the stream. 254 * 255 * @deprecated As of Jinterface 1.4, replaced by super.size(). 256 * @see #size() 257 */ 258 259 @Deprecated count()260 public int count() { 261 return count; 262 } 263 264 /** 265 * Write the low two bytes of a value to the stream in big endian order. 266 * 267 * @param n 268 * the value to use. 269 */ write2BE(final long n)270 public void write2BE(final long n) { 271 write((byte) ((n & 0xff00) >> 8)); 272 write((byte) (n & 0xff)); 273 } 274 275 /** 276 * Write the low four bytes of a value to the stream in big endian order. 277 * 278 * @param n 279 * the value to use. 280 */ write4BE(final long n)281 public void write4BE(final long n) { 282 write((byte) ((n & 0xff000000) >> 24)); 283 write((byte) ((n & 0xff0000) >> 16)); 284 write((byte) ((n & 0xff00) >> 8)); 285 write((byte) (n & 0xff)); 286 } 287 288 /** 289 * Write the low eight (all) bytes of a value to the stream in big endian 290 * order. 291 * 292 * @param n 293 * the value to use. 294 */ write8BE(final long n)295 public void write8BE(final long n) { 296 write((byte) (n >> 56 & 0xff)); 297 write((byte) (n >> 48 & 0xff)); 298 write((byte) (n >> 40 & 0xff)); 299 write((byte) (n >> 32 & 0xff)); 300 write((byte) (n >> 24 & 0xff)); 301 write((byte) (n >> 16 & 0xff)); 302 write((byte) (n >> 8 & 0xff)); 303 write((byte) (n & 0xff)); 304 } 305 306 /** 307 * Write any number of bytes in little endian format. 308 * 309 * @param n 310 * the value to use. 311 * @param b 312 * the number of bytes to write from the little end. 313 */ writeLE(final long n, final int b)314 public void writeLE(final long n, final int b) { 315 long v = n; 316 for (int i = 0; i < b; i++) { 317 write((byte) (v & 0xff)); 318 v >>= 8; 319 } 320 } 321 322 /** 323 * Write the low two bytes of a value to the stream in little endian order. 324 * 325 * @param n 326 * the value to use. 327 */ write2LE(final long n)328 public void write2LE(final long n) { 329 write((byte) (n & 0xff)); 330 write((byte) ((n & 0xff00) >> 8)); 331 } 332 333 /** 334 * Write the low four bytes of a value to the stream in little endian order. 335 * 336 * @param n 337 * the value to use. 338 */ write4LE(final long n)339 public void write4LE(final long n) { 340 write((byte) (n & 0xff)); 341 write((byte) ((n & 0xff00) >> 8)); 342 write((byte) ((n & 0xff0000) >> 16)); 343 write((byte) ((n & 0xff000000) >> 24)); 344 } 345 346 /** 347 * Write the low eight bytes of a value to the stream in little endian 348 * order. 349 * 350 * @param n 351 * the value to use. 352 */ write8LE(final long n)353 public void write8LE(final long n) { 354 write((byte) (n & 0xff)); 355 write((byte) (n >> 8 & 0xff)); 356 write((byte) (n >> 16 & 0xff)); 357 write((byte) (n >> 24 & 0xff)); 358 write((byte) (n >> 32 & 0xff)); 359 write((byte) (n >> 40 & 0xff)); 360 write((byte) (n >> 48 & 0xff)); 361 write((byte) (n >> 56 & 0xff)); 362 } 363 364 /** 365 * Write the low four bytes of a value to the stream in bif endian order, at 366 * the specified position. If the position specified is beyond the end of 367 * the stream, this method will have no effect. 368 * 369 * Normally this method should be used in conjunction with {@link #size() 370 * size()}, when is is necessary to insert data into the stream before it is 371 * known what the actual value should be. For example: 372 * 373 * <pre> 374 * int pos = s.size(); 375 * s.write4BE(0); // make space for length data, 376 * // but final value is not yet known 377 * [ ...more write statements...] 378 * // later... when we know the length value 379 * s.poke4BE(pos, length); 380 * </pre> 381 * 382 * 383 * @param offset 384 * the position in the stream. 385 * @param n 386 * the value to use. 387 */ poke4BE(final int offset, final long n)388 public void poke4BE(final int offset, final long n) { 389 if (offset < super.count) { 390 buf[offset + 0] = (byte) ((n & 0xff000000) >> 24); 391 buf[offset + 1] = (byte) ((n & 0xff0000) >> 16); 392 buf[offset + 2] = (byte) ((n & 0xff00) >> 8); 393 buf[offset + 3] = (byte) (n & 0xff); 394 } 395 } 396 397 /** 398 * Write a string to the stream as an Erlang atom. 399 * 400 * @param atom 401 * the string to write. 402 */ write_atom(final String atom)403 public void write_atom(final String atom) { 404 String enc_atom; 405 byte[] bytes; 406 407 if (atom.codePointCount(0, atom.length()) <= OtpExternal.maxAtomLength) { 408 enc_atom = atom; 409 } else { 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 enc_atom = new String(OtpErlangString.stringToCodePoints(atom), 0, 415 OtpExternal.maxAtomLength); 416 } 417 418 try { 419 bytes = enc_atom.getBytes("UTF-8"); 420 final int length = bytes.length; 421 if (length < 256) { 422 write1(OtpExternal.smallAtomUtf8Tag); 423 write1(length); 424 } else { 425 write1(OtpExternal.atomUtf8Tag); 426 write2BE(length); 427 } 428 writeN(bytes); 429 } catch (final java.io.UnsupportedEncodingException e) { 430 /* 431 * Sigh, why didn't the API designer add an OtpErlangEncodeException 432 * to these encoding functions?!? Instead of changing the API we 433 * write an invalid atom and let it fail for whoever trying to 434 * decode this... Sigh, again... 435 */ 436 write1(OtpExternal.smallAtomUtf8Tag); 437 write1(2); 438 write2BE(0xffff); /* Invalid UTF-8 */ 439 } 440 } 441 442 /** 443 * Write an array of bytes to the stream as an Erlang binary. 444 * 445 * @param bin 446 * the array of bytes to write. 447 */ write_binary(final byte[] bin)448 public void write_binary(final byte[] bin) { 449 write1(OtpExternal.binTag); 450 write4BE(bin.length); 451 writeN(bin); 452 } 453 454 /** 455 * Write an array of bytes to the stream as an Erlang bitstr. 456 * 457 * @param bin 458 * the array of bytes to write. 459 * @param pad_bits 460 * the number of zero pad bits at the low end of the last byte 461 */ write_bitstr(final byte[] bin, final int pad_bits)462 public void write_bitstr(final byte[] bin, final int pad_bits) { 463 if (pad_bits == 0) { 464 write_binary(bin); 465 return; 466 } 467 write1(OtpExternal.bitBinTag); 468 write4BE(bin.length); 469 write1(8 - pad_bits); 470 writeN(bin); 471 } 472 473 /** 474 * Write a boolean value to the stream as the Erlang atom 'true' or 'false'. 475 * 476 * @param b 477 * the boolean value to write. 478 */ write_boolean(final boolean b)479 public void write_boolean(final boolean b) { 480 write_atom(String.valueOf(b)); 481 } 482 483 /** 484 * Write a single byte to the stream as an Erlang integer. The byte is 485 * really an IDL 'octet', that is, unsigned. 486 * 487 * @param b 488 * the byte to use. 489 */ write_byte(final byte b)490 public void write_byte(final byte b) { 491 this.write_long(b & 0xffL, true); 492 } 493 494 /** 495 * Write a character to the stream as an Erlang integer. The character may 496 * be a 16 bit character, kind of IDL 'wchar'. It is up to the Erlang side 497 * to take care of souch, if they should be used. 498 * 499 * @param c 500 * the character to use. 501 */ write_char(final char c)502 public void write_char(final char c) { 503 this.write_long(c & 0xffffL, true); 504 } 505 506 /** 507 * Write a double value to the stream. 508 * 509 * @param d 510 * the double to use. 511 */ write_double(final double d)512 public void write_double(final double d) { 513 write1(OtpExternal.newFloatTag); 514 write8BE(Double.doubleToLongBits(d)); 515 } 516 517 /** 518 * Write a float value to the stream. 519 * 520 * @param f 521 * the float to use. 522 */ write_float(final float f)523 public void write_float(final float f) { 524 write_double(f); 525 } 526 write_big_integer(final BigInteger v)527 public void write_big_integer(final BigInteger v) { 528 if (v.bitLength() < 64) { 529 this.write_long(v.longValue(), true); 530 return; 531 } 532 final int signum = v.signum(); 533 BigInteger val = v; 534 if (signum < 0) { 535 val = val.negate(); 536 } 537 final byte[] magnitude = val.toByteArray(); 538 final int n = magnitude.length; 539 // Reverse the array to make it little endian. 540 for (int i = 0, j = n; i < j--; i++) { 541 // Swap [i] with [j] 542 final byte b = magnitude[i]; 543 magnitude[i] = magnitude[j]; 544 magnitude[j] = b; 545 } 546 if ((n & 0xFF) == n) { 547 write1(OtpExternal.smallBigTag); 548 write1(n); // length 549 } else { 550 write1(OtpExternal.largeBigTag); 551 write4BE(n); // length 552 } 553 write1(signum < 0 ? 1 : 0); // sign 554 // Write the array 555 writeN(magnitude); 556 } 557 558 void write_long(final long v, final boolean unsigned) { 559 /* 560 * If v<0 and unsigned==true the value 561 * java.lang.Long.MAX_VALUE-java.lang.Long.MIN_VALUE+1+v is written, i.e 562 * v is regarded as unsigned two's complement. 563 */ 564 if ((v & 0xffL) == v) { 565 // will fit in one byte 566 write1(OtpExternal.smallIntTag); 567 write1(v); 568 } else { 569 // note that v != 0L 570 if (v < 0 && unsigned || v < OtpExternal.erlMin 571 || v > OtpExternal.erlMax) { 572 // some kind of bignum 573 final long abs = unsigned ? v : v < 0 ? -v : v; 574 final int sign = unsigned ? 0 : v < 0 ? 1 : 0; 575 int n; 576 long mask; 577 for (mask = 0xFFFFffffL, n = 4; (abs & mask) != abs; n++, mask = mask << 8 | 0xffL) { 578 // count nonzero bytes 579 } 580 write1(OtpExternal.smallBigTag); 581 write1(n); // length 582 write1(sign); // sign 583 writeLE(abs, n); // value. obs! little endian 584 } else { 585 write1(OtpExternal.intTag); 586 write4BE(v); 587 } 588 } 589 } 590 591 /** 592 * Write a long to the stream. 593 * 594 * @param l 595 * the long to use. 596 */ 597 public void write_long(final long l) { 598 this.write_long(l, false); 599 } 600 601 /** 602 * Write a positive long to the stream. The long is interpreted as a two's 603 * complement unsigned long even if it is negative. 604 * 605 * @param ul 606 * the long to use. 607 */ 608 public void write_ulong(final long ul) { 609 this.write_long(ul, true); 610 } 611 612 /** 613 * Write an integer to the stream. 614 * 615 * @param i 616 * the integer to use. 617 */ 618 public void write_int(final int i) { 619 this.write_long(i, false); 620 } 621 622 /** 623 * Write a positive integer to the stream. The integer is interpreted as a 624 * two's complement unsigned integer even if it is negative. 625 * 626 * @param ui 627 * the integer to use. 628 */ 629 public void write_uint(final int ui) { 630 this.write_long(ui & 0xFFFFffffL, true); 631 } 632 633 /** 634 * Write a short to the stream. 635 * 636 * @param s 637 * the short to use. 638 */ 639 public void write_short(final short s) { 640 this.write_long(s, false); 641 } 642 643 /** 644 * Write a positive short to the stream. The short is interpreted as a two's 645 * complement unsigned short even if it is negative. 646 * 647 * @param us 648 * the short to use. 649 */ 650 public void write_ushort(final short us) { 651 this.write_long(us & 0xffffL, true); 652 } 653 654 /** 655 * Write an Erlang list header to the stream. After calling this method, you 656 * must write 'arity' elements to the stream followed by nil, or it will not 657 * be possible to decode it later. 658 * 659 * @param arity 660 * the number of elements in the list. 661 */ 662 public void write_list_head(final int arity) { 663 if (arity == 0) { 664 write_nil(); 665 } else { 666 write1(OtpExternal.listTag); 667 write4BE(arity); 668 } 669 } 670 671 /** 672 * Write an empty Erlang list to the stream. 673 */ 674 public void write_nil() { 675 write1(OtpExternal.nilTag); 676 } 677 678 /** 679 * Write an Erlang tuple header to the stream. After calling this method, 680 * you must write 'arity' elements to the stream or it will not be possible 681 * to decode it later. 682 * 683 * @param arity 684 * the number of elements in the tuple. 685 */ 686 public void write_tuple_head(final int arity) { 687 if (arity < 0xff) { 688 write1(OtpExternal.smallTupleTag); 689 write1(arity); 690 } else { 691 write1(OtpExternal.largeTupleTag); 692 write4BE(arity); 693 } 694 } 695 696 /** 697 * Write an Erlang PID to the stream. 698 * 699 * @param node 700 * the nodename. 701 * 702 * @param id 703 * an arbitrary number. Only the low order 15 bits will be used. 704 * 705 * @param serial 706 * another arbitrary number. Only the low order 13 bits will be 707 * used. 708 * 709 * @param creation 710 * yet another arbitrary number. Only the low order 2 bits will 711 * be used. 712 * 713 */ 714 public void write_pid(final String node, final int id, final int serial, 715 final int creation) { 716 write1(OtpExternal.pidTag); 717 write_atom(node); 718 write4BE(id & 0x7fff); // 15 bits 719 write4BE(serial & 0x1fff); // 13 bits 720 write1(creation & 0x3); // 2 bits 721 } 722 723 /** 724 * Write an Erlang PID to the stream. 725 * 726 * @param pid 727 * the pid 728 */ 729 public void write_pid(OtpErlangPid pid) { 730 write1(pid.tag()); 731 write_atom(pid.node()); 732 write4BE(pid.id()); 733 write4BE(pid.serial()); 734 switch (pid.tag()) { 735 case OtpExternal.pidTag: 736 write1(pid.creation()); 737 break; 738 case OtpExternal.newPidTag: 739 write4BE(pid.creation()); 740 break; 741 default: 742 throw new AssertionError("Invalid pid tag " + pid.tag()); 743 } 744 } 745 746 747 /** 748 * Write an Erlang port to the stream. 749 * 750 * @param node 751 * the nodename. 752 * 753 * @param id 754 * an arbitrary number. Only the low order 28 bits will be used. 755 * 756 * @param creation 757 * another arbitrary number. Only the low order 2 bits will 758 * be used. 759 */ 760 public void write_port(final String node, final int id, final int creation) { 761 write1(OtpExternal.portTag); 762 write_atom(node); 763 write4BE(id & 0xfffffff); // 28 bits 764 write1(creation & 0x3); // 2 bits 765 } 766 767 /** 768 * Write an Erlang port to the stream. 769 * 770 * @param port 771 * the port. 772 */ 773 public void write_port(OtpErlangPort port) { 774 write1(port.tag()); 775 write_atom(port.node()); 776 write4BE(port.id()); 777 switch (port.tag()) { 778 case OtpExternal.portTag: 779 write1(port.creation()); 780 break; 781 case OtpExternal.newPortTag: 782 write4BE(port.creation()); 783 break; 784 default: 785 throw new AssertionError("Invalid port tag " + port.tag()); 786 } 787 } 788 789 /** 790 * Write an old style Erlang ref to the stream. 791 * 792 * @param node 793 * the nodename. 794 * 795 * @param id 796 * an arbitrary number. Only the low order 18 bits will be used. 797 * 798 * @param creation 799 * another arbitrary number. 800 * 801 */ 802 public void write_ref(final String node, final int id, final int creation) { 803 /* Always encode as an extended reference; all 804 participating parties are now expected to be 805 able to decode extended references. */ 806 int ids[] = new int[1]; 807 ids[0] = id; 808 write_ref(node, ids, creation); 809 } 810 811 /** 812 * Write an Erlang ref to the stream. 813 * 814 * @param node 815 * the nodename. 816 * 817 * @param ids 818 * an array of arbitrary numbers. Only the low order 18 bits of 819 * the first number will be used. At most three numbers 820 * will be read from the array. 821 * 822 * @param creation 823 * another arbitrary number. Only the low order 2 bits will be used. 824 * 825 */ 826 public void write_ref(final String node, final int[] ids, final int creation) { 827 int arity = ids.length; 828 if (arity > 3) { 829 arity = 3; // max 3 words in ref 830 } 831 832 write1(OtpExternal.newRefTag); 833 834 // how many id values 835 write2BE(arity); 836 837 write_atom(node); 838 839 write1(creation & 0x3); // 2 bits 840 841 // first int gets truncated to 18 bits 842 write4BE(ids[0] & 0x3ffff); 843 844 // remaining ones are left as is 845 for (int i = 1; i < arity; i++) { 846 write4BE(ids[i]); 847 } 848 } 849 850 /** 851 * Write an Erlang ref to the stream. 852 * 853 * @param ref 854 * the reference 855 */ 856 public void write_ref(OtpErlangRef ref) { 857 int[] ids = ref.ids(); 858 int arity = ids.length; 859 860 write1(ref.tag()); 861 write2BE(arity); 862 write_atom(ref.node()); 863 864 switch (ref.tag()) { 865 case OtpExternal.newRefTag: 866 write1(ref.creation()); 867 write4BE(ids[0] & 0x3ffff); // first word gets truncated to 18 bits 868 break; 869 case OtpExternal.newerRefTag: 870 write4BE(ref.creation()); 871 write4BE(ids[0]); // full first word 872 break; 873 default: 874 throw new AssertionError("Invalid ref tag " + ref.tag()); 875 } 876 877 for (int i = 1; i < arity; i++) { 878 write4BE(ids[i]); 879 } 880 } 881 882 /** 883 * Write a string to the stream. 884 * 885 * @param s 886 * the string to write. 887 */ 888 public void write_string(final String s) { 889 final int len = s.length(); 890 891 switch (len) { 892 case 0: 893 write_nil(); 894 break; 895 default: 896 if (len <= 65535 && is8bitString(s)) { // 8-bit string 897 try { 898 final byte[] bytebuf = s.getBytes("ISO-8859-1"); 899 write1(OtpExternal.stringTag); 900 write2BE(len); 901 writeN(bytebuf); 902 } catch (final UnsupportedEncodingException e) { 903 write_nil(); // it should never ever get here... 904 } 905 } else { // unicode or longer, must code as list 906 final int[] codePoints = OtpErlangString.stringToCodePoints(s); 907 write_list_head(codePoints.length); 908 for (final int codePoint : codePoints) { 909 write_int(codePoint); 910 } 911 write_nil(); 912 } 913 } 914 } 915 916 private boolean is8bitString(final String s) { 917 for (int i = 0; i < s.length(); ++i) { 918 final char c = s.charAt(i); 919 if (c < 0 || c > 255) { 920 return false; 921 } 922 } 923 return true; 924 } 925 926 /** 927 * Write an arbitrary Erlang term to the stream in compressed format. 928 * 929 * @param o 930 * the Erlang term to write. 931 */ 932 public void write_compressed(final OtpErlangObject o) { 933 write_compressed(o, Deflater.DEFAULT_COMPRESSION); 934 } 935 936 /** 937 * Write an arbitrary Erlang term to the stream in compressed format. 938 * 939 * @param o 940 * the Erlang term to write. 941 * @param level 942 * the compression level (<tt>0..9</tt>) 943 */ 944 public void write_compressed(final OtpErlangObject o, final int level) { 945 @SuppressWarnings("resource") 946 final OtpOutputStream oos = new OtpOutputStream(o); 947 /* 948 * similar to erts_term_to_binary() in external.c: We don't want to 949 * compress if compression actually increases the size. Since 950 * compression uses 5 extra bytes (COMPRESSED tag + size), don't 951 * compress if the original term is smaller. 952 */ 953 if (oos.size() < 5) { 954 // fast path for small terms 955 try { 956 oos.writeToAndFlush(this); 957 // if the term is written as a compressed term, the output 958 // stream is closed, so we do this here, too 959 close(); 960 } catch (final IOException e) { 961 throw new java.lang.IllegalArgumentException( 962 "Intermediate stream failed for Erlang object " + o); 963 } 964 } else { 965 final int startCount = super.count; 966 // we need destCount bytes for an uncompressed term 967 // -> if compression uses more, use the uncompressed term! 968 final int destCount = startCount + oos.size(); 969 fixedSize = destCount; 970 final Deflater def = new Deflater(level); 971 final java.util.zip.DeflaterOutputStream dos = new java.util.zip.DeflaterOutputStream( 972 this, def); 973 try { 974 write1(OtpExternal.compressedTag); 975 write4BE(oos.size()); 976 oos.writeTo(dos); 977 dos.close(); // note: closes this, too! 978 } catch (final IllegalArgumentException e) { 979 /* 980 * Discard further un-compressed data (if not called, there may 981 * be memory leaks). 982 * 983 * After calling java.util.zip.Deflater.end(), the deflater 984 * should not be used anymore, not even the close() method of 985 * dos. Calling dos.close() before def.end() is prevented since 986 * an unfinished DeflaterOutputStream will try to deflate its 987 * unprocessed data to the (fixed) byte array which is prevented 988 * by ensureCapacity() and would also unnecessarily process 989 * further data that is discarded anyway. 990 * 991 * Since we are re-using the byte array of this object below, we 992 * must not call close() in e.g. a finally block either (with or 993 * without a call to def.end()). 994 */ 995 def.end(); 996 // could not make the value smaller than originally 997 // -> reset to starting count, write uncompressed 998 super.count = startCount; 999 try { 1000 oos.writeTo(this); 1001 // if the term is written as a compressed term, the output 1002 // stream is closed, so we do this here, too 1003 close(); 1004 } catch (final IOException e2) { 1005 throw new java.lang.IllegalArgumentException( 1006 "Intermediate stream failed for Erlang object " + o); 1007 } 1008 } catch (final IOException e) { 1009 throw new java.lang.IllegalArgumentException( 1010 "Intermediate stream failed for Erlang object " + o); 1011 } finally { 1012 fixedSize = Integer.MAX_VALUE; 1013 } 1014 } 1015 } 1016 1017 /** 1018 * Write an arbitrary Erlang term to the stream. 1019 * 1020 * @param o 1021 * the Erlang term to write. 1022 */ 1023 public void write_any(final OtpErlangObject o) { 1024 // calls one of the above functions, depending on o 1025 o.encode(this); 1026 } 1027 1028 public void write_fun(final OtpErlangPid pid, final String module, 1029 final long old_index, final int arity, final byte[] md5, 1030 final long index, final long uniq, final OtpErlangObject[] freeVars) { 1031 if (arity == -1) { 1032 write1(OtpExternal.funTag); 1033 write4BE(freeVars.length); 1034 pid.encode(this); 1035 write_atom(module); 1036 write_long(index); 1037 write_long(uniq); 1038 for (final OtpErlangObject fv : freeVars) { 1039 fv.encode(this); 1040 } 1041 } else { 1042 write1(OtpExternal.newFunTag); 1043 final int saveSizePos = getPos(); 1044 write4BE(0); // this is where we patch in the size 1045 write1(arity); 1046 writeN(md5); 1047 write4BE(index); 1048 write4BE(freeVars.length); 1049 write_atom(module); 1050 write_long(old_index); 1051 write_long(uniq); 1052 pid.encode(this); 1053 for (final OtpErlangObject fv : freeVars) { 1054 fv.encode(this); 1055 } 1056 poke4BE(saveSizePos, getPos() - saveSizePos); 1057 } 1058 } 1059 1060 public void write_external_fun(final String module, final String function, 1061 final int arity) { 1062 write1(OtpExternal.externalFunTag); 1063 write_atom(module); 1064 write_atom(function); 1065 write_long(arity); 1066 } 1067 1068 public void write_map_head(final int arity) { 1069 write1(OtpExternal.mapTag); 1070 write4BE(arity); 1071 } 1072 } 1073