PageRenderTime 59ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/otp.net/Otp/OtpOutputStream.cs

https://github.com/bmizerany/jungerl
C# | 776 lines | 372 code | 67 blank | 337 comment | 31 complexity | 5e53a28473b1d51ab2ea7b2120250faa MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Converted from Java to C# by Vlad Dumitrescu (vlad_Dumitrescu@hotmail.com)
  17. */
  18. namespace Otp
  19. {
  20. using System;
  21. /*
  22. * Provides a stream for encoding Erlang terms to external format, for
  23. * transmission or storage.
  24. *
  25. * <p> Note that this class is not synchronized, if you need
  26. * synchronization you must provide it yourself.
  27. *
  28. **/
  29. public class OtpOutputStream
  30. {
  31. /*The default initial size of the stream. **/
  32. public const int defaultInitialSize = 2048;
  33. /*The default increment used when growing the stream. **/
  34. public const int defaultIncrement = 2048;
  35. private byte[] buf = null;
  36. private int _size = 0;
  37. private int _count = 0;
  38. /*
  39. * Create a stream with the default initial size.
  40. **/
  41. public OtpOutputStream():this(defaultInitialSize)
  42. {
  43. }
  44. /*
  45. * Create a stream with the specified initial size.
  46. **/
  47. public OtpOutputStream(int size)
  48. {
  49. this._size = size;
  50. buf = new byte[size];
  51. _count = 0;
  52. }
  53. /*
  54. * Create a stream containing the encoded version of the given
  55. * Erlang term.
  56. **/
  57. public OtpOutputStream(Erlang.Object o):this()
  58. {
  59. this.write_any(o);
  60. }
  61. // package scope
  62. /*
  63. * Get the contents of the output stream as an input stream instead.
  64. * This is used internally in {@link OtpCconnection} for tracing
  65. * outgoing packages.
  66. *
  67. * @param offset where in the output stream to read data from when
  68. * creating the input stream. The offset is necessary because header
  69. * contents start 5 bytes into the header buffer, whereas payload
  70. * contents start at the beginning
  71. *
  72. * @return an input stream containing the same raw data.
  73. **/
  74. internal virtual OtpInputStream getOtpInputStream(int offset)
  75. {
  76. return new OtpInputStream(buf, offset, _count - offset);
  77. }
  78. /*
  79. * Reset the stream so that it can be reused.
  80. **/
  81. public virtual void reset()
  82. {
  83. _count = 0;
  84. }
  85. /*
  86. * Get the current position in the stream.
  87. *
  88. * @return the current position in the stream.
  89. **/
  90. public virtual int getPos()
  91. {
  92. return _count;
  93. }
  94. /*
  95. * Write one byte to the stream.
  96. *
  97. * @param b the byte to write.
  98. *
  99. **/
  100. public virtual void write(byte b)
  101. {
  102. if (_count >= _size)
  103. {
  104. // System.err.println("Expanding buffer from " + size + " to " + size + defaultIncrement);
  105. byte[] tmp = new byte[_size + defaultIncrement];
  106. Array.Copy(buf, 0, tmp, 0, _count);
  107. _size += defaultIncrement;
  108. buf = tmp;
  109. }
  110. buf[_count++] = b;
  111. }
  112. /*
  113. * Write an array of bytes to the stream.
  114. *
  115. * @param buf the array of bytes to write.
  116. *
  117. **/
  118. public virtual void write(byte[] buf)
  119. {
  120. if (_count + buf.Length > _size)
  121. {
  122. // System.err.println("Expanding buffer from " +
  123. // size + " to " + buf.length + size + defaultIncrement);
  124. byte[] tmp = new byte[_size + buf.Length + defaultIncrement];
  125. Array.Copy(this.buf, 0, tmp, 0, _count);
  126. _size += defaultIncrement + buf.Length;
  127. this.buf = tmp;
  128. }
  129. Array.Copy(buf, 0, this.buf, _count, buf.Length);
  130. _count += (int) (buf.Length);
  131. }
  132. /*
  133. * Write the contents of the stream to an OutputStream.
  134. *
  135. * @param os the OutputStream to write to.
  136. *
  137. * @exception C#.io.IOException if there is an error writing to
  138. * the OutputStream.
  139. **/
  140. public virtual void writeTo(System.IO.Stream os)
  141. {
  142. os.Write(buf, 0, _count);
  143. os.Flush();
  144. }
  145. /*
  146. * Write the low byte of a value to the stream.
  147. *
  148. * @param n the value to use.
  149. *
  150. **/
  151. public virtual void write1(long n)
  152. {
  153. write((byte) (n & 0xff));
  154. }
  155. /*
  156. * Write an array of bytes to the stream.
  157. *
  158. * @param buf the array of bytes to write.
  159. *
  160. **/
  161. public virtual void writeN(byte[] bytes)
  162. {
  163. write(bytes);
  164. }
  165. /*
  166. * Get the current capacity of the stream. As bytes are added the
  167. * capacity of the stream is increased automatically, however this
  168. * method returns the current size.
  169. *
  170. * @return the size of the internal buffer used by the stream.
  171. **/
  172. public virtual int size()
  173. {
  174. return _size;
  175. }
  176. /*
  177. * Get the number of bytes in the stream.
  178. *
  179. * @return the number of bytes in the stream.
  180. **/
  181. public virtual int count()
  182. {
  183. return _count;
  184. }
  185. /*
  186. * Write the low two bytes of a value to the stream in big endian
  187. * order.
  188. *
  189. * @param n the value to use.
  190. **/
  191. public virtual void write2BE(long n)
  192. {
  193. write((byte) ((n & 0xff00) >> 8));
  194. write((byte) (n & 0xff));
  195. }
  196. /*
  197. * Write the low four bytes of a value to the stream in big endian
  198. * order.
  199. *
  200. * @param n the value to use.
  201. **/
  202. public virtual void write4BE(long n)
  203. {
  204. write((byte) ((n & 0xff000000) >> 24));
  205. write((byte) ((n & 0xff0000) >> 16));
  206. write((byte) ((n & 0xff00) >> 8));
  207. write((byte) (n & 0xff));
  208. }
  209. /*
  210. * Write the low two bytes of a value to the stream in little endian
  211. * order.
  212. *
  213. * @param n the value to use.
  214. **/
  215. public virtual void write2LE(long n)
  216. {
  217. write((byte) (n & 0xff));
  218. write((byte) ((n & 0xff00) >> 8));
  219. }
  220. /*
  221. * Write the low four bytes of a value to the stream in little
  222. * endian order.
  223. *
  224. * @param n the value to use.
  225. **/
  226. public virtual void write4LE(long n)
  227. {
  228. write((byte) (n & 0xff));
  229. write((byte) ((n & 0xff00) >> 8));
  230. write((byte) ((n & 0xff0000) >> 16));
  231. write((byte) ((n & 0xff000000) >> 24));
  232. }
  233. /*
  234. * Write the low four bytes of a value to the stream in bif endian
  235. * order, at the specified position. If the position specified is
  236. * beyond the end of the stream, this method will have no effect.
  237. *
  238. * Normally this method should be used in conjunction with {@link
  239. * #getPos() getPos()}, when is is necessary to insert data into the
  240. * stream before it is known what the actual value should be. For
  241. * example:
  242. *
  243. <pre>
  244. int pos = s.getPos();
  245. s.write4BE(0); // make space for length data,
  246. // but final value is not yet known
  247. [ ...more write statements...]
  248. // later... when we know the length value
  249. s.poke4BE(pos, length);
  250. </pre>
  251. *
  252. * @param offset the position in the stream.
  253. * @param n the value to use.
  254. **/
  255. public virtual void poke4BE(int offset, long n)
  256. {
  257. if (offset < _count)
  258. {
  259. buf[offset + 0] = ((byte) ((n & 0xff000000) >> 24));
  260. buf[offset + 1] = ((byte) ((n & 0xff0000) >> 16));
  261. buf[offset + 2] = ((byte) ((n & 0xff00) >> 8));
  262. buf[offset + 3] = ((byte) (n & 0xff));
  263. }
  264. }
  265. /*
  266. * Write a string to the stream as an Erlang atom.
  267. *
  268. * @param atom the string to write.
  269. **/
  270. public virtual void write_atom(System.String atom)
  271. {
  272. this.write1(OtpExternal.atomTag);
  273. this.write2BE(atom.Length);
  274. //UPGRADE_NOTE: This code will be optimized in the future;
  275. byte[] tmpBytes;
  276. int i;
  277. string tmpStr;
  278. tmpStr = atom;
  279. tmpBytes = new byte[tmpStr.Length];
  280. i = 0;
  281. while (i < tmpStr.Length)
  282. {
  283. tmpBytes[i] = (byte) tmpStr[i];
  284. i++;
  285. }
  286. this.writeN(tmpBytes);
  287. }
  288. /*
  289. * Write an array of bytes to the stream as an Erlang binary.
  290. *
  291. * @param bin the array of bytes to write.
  292. **/
  293. public virtual void write_binary(byte[] bin)
  294. {
  295. this.write1(OtpExternal.binTag);
  296. this.write4BE(bin.Length);
  297. this.writeN(bin);
  298. }
  299. /*
  300. * Write a boolean value to the stream as the Erlang atom 'true' or
  301. * 'false'.
  302. *
  303. * @param b the boolean value to write.
  304. **/
  305. public virtual void write_boolean(bool b)
  306. {
  307. this.write_atom(b.ToString());
  308. }
  309. /*
  310. * Write a single byte to the stream as an Erlang integer.
  311. *
  312. * @param b the byte to use.
  313. **/
  314. public virtual void write_byte(byte b)
  315. {
  316. this.write_long(b);
  317. }
  318. /*
  319. * Write a character to the stream as an Erlang integer.
  320. *
  321. * @param c the character to use.
  322. **/
  323. public virtual void write_char(char c)
  324. {
  325. this.write_long(c);
  326. }
  327. /*
  328. * Write a double value to the stream.
  329. *
  330. * @param d the double to use.
  331. **/
  332. public virtual void write_double(double d)
  333. {
  334. double val;
  335. int exp = 0;
  336. int sign = 0;
  337. System.String str;
  338. // remove sign to simplify decimal shift
  339. if (d >= 0)
  340. {
  341. val = d;
  342. }
  343. else
  344. {
  345. sign = 1;
  346. val = - d;
  347. }
  348. // move the decimal point until we have a single units digit
  349. if (System.Math.Sign(val) != 0)
  350. {
  351. // until greater than or equal to 1.0 -> multiply by 10
  352. while (val < 1.0)
  353. {
  354. val *= 10;
  355. exp--;
  356. }
  357. // until strictly less than 10.0 -> divide by 10
  358. while (val >= 10.0)
  359. {
  360. val /= 10;
  361. exp++;
  362. }
  363. }
  364. // get 20 decimal digits, put sign back, add new exponent
  365. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.ROUND_HALF_EVEN will be considered in a future release.;
  366. //val = val.setScale(20, BigDecimal.ROUND_HALF_EVEN);
  367. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.toString will be considered in a future release.;
  368. str = (sign == 1?"-":"") + System.Convert.ToString(val) + "e" + System.Convert.ToString(exp);
  369. // write the value
  370. this.write1(OtpExternal.floatTag);
  371. //UPGRADE_NOTE: This code will be optimized in the future;
  372. byte[] tmpBytes;
  373. int i;
  374. string tmpStr;
  375. tmpStr = str;
  376. tmpBytes = new byte[tmpStr.Length];
  377. i = 0;
  378. while (i < tmpStr.Length)
  379. {
  380. tmpBytes[i] = (byte) tmpStr[i];
  381. i++;
  382. }
  383. this.writeN(tmpBytes);
  384. // pad with zeros to 31 bytes
  385. //UPGRADE_NOTE: This code will be optimized in the future;
  386. byte[] tmpBytes2;
  387. int i2;
  388. string tmpStr2;
  389. tmpStr2 = str;
  390. tmpBytes2 = new byte[tmpStr2.Length];
  391. i2 = 0;
  392. while (i2 < tmpStr2.Length)
  393. {
  394. tmpBytes2[i2] = (byte) tmpStr2[i2];
  395. i2++;
  396. }
  397. int i3 = (int) (tmpBytes2.Length);
  398. for (; i3 < 31; i3++)
  399. this.write1(0);
  400. }
  401. /*
  402. * Write a float value to the stream.
  403. *
  404. * @param f the float to use.
  405. **/
  406. public virtual void write_float(float f)
  407. {
  408. this.write_double(f);
  409. }
  410. /*
  411. * Write a long to the stream.
  412. *
  413. * @param l the long to use.
  414. **/
  415. public virtual void write_long(long l)
  416. {
  417. if ((l & 0xff) == l)
  418. {
  419. // will fit in one byte
  420. this.write1(OtpExternal.smallIntTag);
  421. this.write1(l);
  422. }
  423. else if ((l <= OtpExternal.erlMax) && (l >= OtpExternal.erlMin))
  424. {
  425. this.write1(OtpExternal.intTag);
  426. this.write4BE(l);
  427. }
  428. else
  429. {
  430. this.write1(OtpExternal.smallBigTag);
  431. this.write1(4); // length
  432. // obs! little endian here
  433. if (l < 0)
  434. {
  435. this.write1(1); // sign
  436. this.write4LE(- l); // value
  437. }
  438. else
  439. {
  440. this.write1(0); // sign
  441. this.write4LE(l); //value
  442. }
  443. }
  444. }
  445. /*
  446. * Write a positive long to the stream.
  447. *
  448. * @param ul the long to use.
  449. **/
  450. public virtual void write_ulong(long ul)
  451. {
  452. this.write_long(ul);
  453. }
  454. /*
  455. * Write an integer to the stream.
  456. *
  457. * @param i the integer to use.
  458. **/
  459. public virtual void write_int(int i)
  460. {
  461. this.write_long(i);
  462. }
  463. /*
  464. * Write a positive integer to the stream.
  465. *
  466. * @param ui the integer to use.
  467. **/
  468. public virtual void write_uint(int ui)
  469. {
  470. this.write_long(ui);
  471. }
  472. /*
  473. * Write a short to the stream.
  474. *
  475. * @param s the short to use.
  476. **/
  477. public virtual void write_short(short s)
  478. {
  479. this.write_long(s);
  480. }
  481. /*
  482. * Write a positive short to the stream.
  483. *
  484. * @param s the short to use.
  485. **/
  486. public virtual void write_ushort(short us)
  487. {
  488. this.write_long(us);
  489. }
  490. /*
  491. * Write an Erlang list header to the stream. After calling this
  492. * method, you must write 'arity' elements to the stream followed by
  493. * nil, or it will not be possible to decode it later.
  494. *
  495. * @param arity the number of elements in the list.
  496. **/
  497. public virtual void write_list_head(int arity)
  498. {
  499. if (arity == 0)
  500. {
  501. this.write_nil();
  502. }
  503. else
  504. {
  505. this.write1(OtpExternal.listTag);
  506. this.write4BE(arity);
  507. }
  508. }
  509. /*
  510. * Write an empty Erlang list to the stream.
  511. **/
  512. public virtual void write_nil()
  513. {
  514. this.write1(OtpExternal.nilTag);
  515. }
  516. /*
  517. * Write an Erlang tuple header to the stream. After calling this
  518. * method, you must write 'arity' elements to the stream or it will
  519. * not be possible to decode it later.
  520. *
  521. * @param arity the number of elements in the tuple.
  522. **/
  523. public virtual void write_tuple_head(int arity)
  524. {
  525. if (arity < 0xff)
  526. {
  527. this.write1(OtpExternal.smallTupleTag);
  528. this.write1(arity);
  529. }
  530. else
  531. {
  532. this.write1(OtpExternal.largeTupleTag);
  533. this.write4BE(arity);
  534. }
  535. }
  536. /*
  537. * Write an Erlang PID to the stream.
  538. *
  539. * @param node the nodename.
  540. *
  541. * @param id an arbitrary number. Only the low order 15 bits will
  542. * be used.
  543. *
  544. * @param serial another arbitrary number. Only the low order 3 bits
  545. * will be used.
  546. *
  547. * @param creation yet another arbitrary number. Only the low order
  548. * 2 bits will be used.
  549. *
  550. **/
  551. public virtual void write_pid(System.String node, int id, int serial, int creation)
  552. {
  553. this.write1(OtpExternal.pidTag);
  554. this.write_atom(node);
  555. this.write4BE(id & 0x7fff); // 15 bits
  556. this.write4BE(serial & 0x7); // 3 bits
  557. this.write1(creation & 0x3); // 2 bits
  558. }
  559. /*
  560. * Write an Erlang port to the stream.
  561. *
  562. * @param node the nodename.
  563. *
  564. * @param id an arbitrary number. Only the low order 18 bits will
  565. * be used.
  566. *
  567. * @param creation another arbitrary number. Only the low order 2
  568. * bits will be used.
  569. *
  570. **/
  571. public virtual void write_port(System.String node, int id, int creation)
  572. {
  573. this.write1(OtpExternal.portTag);
  574. this.write_atom(node);
  575. this.write4BE(id & 0x3ffff); // 18 bits
  576. this.write1(creation & 0x3); // 2 bits
  577. }
  578. /*
  579. * Write an old style Erlang ref to the stream.
  580. *
  581. * @param node the nodename.
  582. *
  583. * @param id an arbitrary number. Only the low order 18 bits will
  584. * be used.
  585. *
  586. * @param creation another arbitrary number. Only the low order 2
  587. * bits will be used.
  588. *
  589. **/
  590. public virtual void write_ref(System.String node, int id, int creation)
  591. {
  592. this.write1(OtpExternal.refTag);
  593. this.write_atom(node);
  594. this.write4BE(id & 0x3ffff); // 18 bits
  595. this.write1(creation & 0x3); // 2 bits
  596. }
  597. /*
  598. * Write a new style (R6 and later) Erlang ref to the stream.
  599. *
  600. * @param node the nodename.
  601. *
  602. * @param ids an array of arbitrary numbers. Only the low order 18
  603. * bits of the first number will be used. If the array contains only
  604. * one number, an old style ref will be written instead. At most
  605. * three numbers will be read from the array.
  606. *
  607. * @param creation another arbitrary number. Only the low order
  608. * 2 bits will be used.
  609. *
  610. **/
  611. public virtual void write_ref(System.String node, int[] ids, int creation)
  612. {
  613. int arity = (int) (ids.Length);
  614. if (arity > 3)
  615. arity = 3;
  616. // max 3 words in ref
  617. if (arity == 1)
  618. {
  619. // use old method
  620. this.write_ref(node, ids[0], creation);
  621. }
  622. else
  623. {
  624. // r6 ref
  625. this.write1(OtpExternal.newRefTag);
  626. // how many id values
  627. this.write2BE(arity);
  628. this.write_atom(node);
  629. // note: creation BEFORE id in r6 ref
  630. this.write1(creation & 0x3); // 2 bits
  631. // first int gets truncated to 18 bits
  632. this.write4BE(ids[0] & 0x3ffff);
  633. // remaining ones are left as is
  634. for (int i = 1; i < arity; i++)
  635. this.write4BE(ids[i]);
  636. }
  637. }
  638. /*
  639. * Write a string to the stream.
  640. *
  641. * @param s the string to write.
  642. **/
  643. public virtual void write_string(System.String s)
  644. {
  645. int len = s.Length;
  646. switch (len)
  647. {
  648. case 0:
  649. this.write_nil();
  650. break;
  651. default:
  652. //UPGRADE_NOTE: This code will be optimized in the future;
  653. byte[] tmpBytes;
  654. int i;
  655. string tmpStr;
  656. tmpStr = s;
  657. tmpBytes = new byte[tmpStr.Length];
  658. i = 0;
  659. while (i < tmpStr.Length)
  660. {
  661. tmpBytes[i] = (byte) tmpStr[i];
  662. i++;
  663. }
  664. byte[] bytebuf = tmpBytes;
  665. /*switch to se if the length of
  666. the byte array is equal to the
  667. length of the list */
  668. if (bytebuf.Length == len)
  669. {
  670. /*Usual */
  671. this.write1(OtpExternal.stringTag);
  672. this.write2BE(len);
  673. this.writeN(bytebuf);
  674. }
  675. else
  676. {
  677. /*Unicode */
  678. char[] charbuf = s.ToCharArray();
  679. this.write_list_head(len);
  680. for (int i2 = 0; i2 < len; i2++)
  681. this.write_char(charbuf[i2]);
  682. this.write_nil();
  683. }
  684. break;
  685. }
  686. }
  687. /*
  688. This does not work when char > 1 byte Unicode is used
  689. public void write_string(String s) {
  690. this.write1(OtpExternal.stringTag);
  691. this.write2BE(s.length());
  692. this.writeN(s.getBytes());
  693. }*/
  694. /*
  695. * Write an arbitrary Erlang term to the stream.
  696. *
  697. * @param o the Erlang term to write.
  698. */
  699. public virtual void write_any(Erlang.Object o)
  700. {
  701. // calls one of the above functions, depending on o
  702. o.encode(this);
  703. }
  704. }
  705. }