PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/gebi/jungerl
C# | 786 lines | 325 code | 63 blank | 398 comment | 23 complexity | a5df338aeadd906360e0e75a6bf20579 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 ? Erlang.Boolean.s_true : Erlang.Boolean.s_false).atomValue());
  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. this.write1(OtpExternal.newFloatTag);
  335. byte[] data = BitConverter.GetBytes(d);
  336. if (BitConverter.IsLittleEndian)
  337. {
  338. Array.Reverse(data);
  339. }
  340. this.write(data);
  341. /*
  342. double val;
  343. int exp = 0;
  344. int sign = 0;
  345. System.String str;
  346. // remove sign to simplify decimal shift
  347. if (d >= 0)
  348. {
  349. val = d;
  350. }
  351. else
  352. {
  353. sign = 1;
  354. val = - d;
  355. }
  356. // move the decimal point until we have a single units digit
  357. if (System.Math.Sign(val) != 0)
  358. {
  359. // until greater than or equal to 1.0 -> multiply by 10
  360. while (val < 1.0)
  361. {
  362. val *= 10;
  363. exp--;
  364. }
  365. // until strictly less than 10.0 -> divide by 10
  366. while (val >= 10.0)
  367. {
  368. val /= 10;
  369. exp++;
  370. }
  371. }
  372. // get 20 decimal digits, put sign back, add new exponent
  373. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.ROUND_HALF_EVEN will be considered in a future release.;
  374. //val = val.setScale(20, BigDecimal.ROUND_HALF_EVEN);
  375. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.toString will be considered in a future release.;
  376. str = (sign == 1?"-":"") + System.Convert.ToString(val) + "e" + System.Convert.ToString(exp);
  377. // write the value
  378. this.write1(OtpExternal.floatTag);
  379. //UPGRADE_NOTE: This code will be optimized in the future;
  380. byte[] tmpBytes;
  381. int i;
  382. string tmpStr;
  383. tmpStr = str;
  384. tmpBytes = new byte[tmpStr.Length];
  385. i = 0;
  386. while (i < tmpStr.Length)
  387. {
  388. tmpBytes[i] = (byte) tmpStr[i];
  389. i++;
  390. }
  391. this.writeN(tmpBytes);
  392. // pad with zeros to 31 bytes
  393. //UPGRADE_NOTE: This code will be optimized in the future;
  394. byte[] tmpBytes2;
  395. int i2;
  396. string tmpStr2;
  397. tmpStr2 = str;
  398. tmpBytes2 = new byte[tmpStr2.Length];
  399. i2 = 0;
  400. while (i2 < tmpStr2.Length)
  401. {
  402. tmpBytes2[i2] = (byte) tmpStr2[i2];
  403. i2++;
  404. }
  405. int i3 = (int) (tmpBytes2.Length);
  406. for (; i3 < 31; i3++)
  407. this.write1(0);
  408. */
  409. }
  410. /*
  411. * Write a float value to the stream.
  412. *
  413. * @param f the float to use.
  414. **/
  415. public virtual void write_float(float f)
  416. {
  417. this.write_double(f);
  418. }
  419. /*
  420. * Write a long to the stream.
  421. *
  422. * @param l the long to use.
  423. **/
  424. public virtual void write_long(long l)
  425. {
  426. if ((l & 0xff) == l)
  427. {
  428. // will fit in one byte
  429. this.write1(OtpExternal.smallIntTag);
  430. this.write1(l);
  431. }
  432. else if ((l <= OtpExternal.erlMax) && (l >= OtpExternal.erlMin))
  433. {
  434. this.write1(OtpExternal.intTag);
  435. this.write4BE(l);
  436. }
  437. else
  438. {
  439. this.write1(OtpExternal.smallBigTag);
  440. this.write1(4); // length
  441. // obs! little endian here
  442. if (l < 0)
  443. {
  444. this.write1(1); // sign
  445. this.write4LE(- l); // value
  446. }
  447. else
  448. {
  449. this.write1(0); // sign
  450. this.write4LE(l); //value
  451. }
  452. }
  453. }
  454. /*
  455. * Write a positive long to the stream.
  456. *
  457. * @param ul the long to use.
  458. **/
  459. public virtual void write_ulong(long ul)
  460. {
  461. this.write_long(ul);
  462. }
  463. /*
  464. * Write an integer to the stream.
  465. *
  466. * @param i the integer to use.
  467. **/
  468. public virtual void write_int(int i)
  469. {
  470. this.write_long(i);
  471. }
  472. /*
  473. * Write a positive integer to the stream.
  474. *
  475. * @param ui the integer to use.
  476. **/
  477. public virtual void write_uint(int ui)
  478. {
  479. this.write_long(ui);
  480. }
  481. /*
  482. * Write a short to the stream.
  483. *
  484. * @param s the short to use.
  485. **/
  486. public virtual void write_short(short s)
  487. {
  488. this.write_long(s);
  489. }
  490. /*
  491. * Write a positive short to the stream.
  492. *
  493. * @param s the short to use.
  494. **/
  495. public virtual void write_ushort(short us)
  496. {
  497. this.write_long(us);
  498. }
  499. /*
  500. * Write an Erlang list header to the stream. After calling this
  501. * method, you must write 'arity' elements to the stream followed by
  502. * nil, or it will not be possible to decode it later.
  503. *
  504. * @param arity the number of elements in the list.
  505. **/
  506. public virtual void write_list_head(int arity)
  507. {
  508. if (arity == 0)
  509. {
  510. this.write_nil();
  511. }
  512. else
  513. {
  514. this.write1(OtpExternal.listTag);
  515. this.write4BE(arity);
  516. }
  517. }
  518. /*
  519. * Write an empty Erlang list to the stream.
  520. **/
  521. public virtual void write_nil()
  522. {
  523. this.write1(OtpExternal.nilTag);
  524. }
  525. /*
  526. * Write an Erlang tuple header to the stream. After calling this
  527. * method, you must write 'arity' elements to the stream or it will
  528. * not be possible to decode it later.
  529. *
  530. * @param arity the number of elements in the tuple.
  531. **/
  532. public virtual void write_tuple_head(int arity)
  533. {
  534. if (arity < 0xff)
  535. {
  536. this.write1(OtpExternal.smallTupleTag);
  537. this.write1(arity);
  538. }
  539. else
  540. {
  541. this.write1(OtpExternal.largeTupleTag);
  542. this.write4BE(arity);
  543. }
  544. }
  545. /*
  546. * Write an Erlang PID to the stream.
  547. *
  548. * @param node the nodename.
  549. *
  550. * @param id an arbitrary number. Only the low order 15 bits will
  551. * be used.
  552. *
  553. * @param serial another arbitrary number. Only the low order 3 bits
  554. * will be used.
  555. *
  556. * @param creation yet another arbitrary number. Only the low order
  557. * 2 bits will be used.
  558. *
  559. **/
  560. public virtual void write_pid(System.String node, int id, int serial, int creation)
  561. {
  562. this.write1(OtpExternal.pidTag);
  563. this.write_atom(node);
  564. this.write4BE(id & 0x7fff); // 15 bits
  565. this.write4BE(serial & 0x7); // 3 bits
  566. this.write1(creation & 0x3); // 2 bits
  567. }
  568. /*
  569. * Write an Erlang port to the stream.
  570. *
  571. * @param node the nodename.
  572. *
  573. * @param id an arbitrary number. Only the low order 18 bits will
  574. * be used.
  575. *
  576. * @param creation another arbitrary number. Only the low order 2
  577. * bits will be used.
  578. *
  579. **/
  580. public virtual void write_port(System.String node, int id, int creation)
  581. {
  582. this.write1(OtpExternal.portTag);
  583. this.write_atom(node);
  584. this.write4BE(id & 0x3ffff); // 18 bits
  585. this.write1(creation & 0x3); // 2 bits
  586. }
  587. /*
  588. * Write an old style Erlang ref to the stream.
  589. *
  590. * @param node the nodename.
  591. *
  592. * @param id an arbitrary number. Only the low order 18 bits will
  593. * be used.
  594. *
  595. * @param creation another arbitrary number. Only the low order 2
  596. * bits will be used.
  597. *
  598. **/
  599. public virtual void write_ref(System.String node, int id, int creation)
  600. {
  601. this.write1(OtpExternal.refTag);
  602. this.write_atom(node);
  603. this.write4BE(id & 0x3ffff); // 18 bits
  604. this.write1(creation & 0x3); // 2 bits
  605. }
  606. /*
  607. * Write a new style (R6 and later) Erlang ref to the stream.
  608. *
  609. * @param node the nodename.
  610. *
  611. * @param ids an array of arbitrary numbers. Only the low order 18
  612. * bits of the first number will be used. If the array contains only
  613. * one number, an old style ref will be written instead. At most
  614. * three numbers will be read from the array.
  615. *
  616. * @param creation another arbitrary number. Only the low order
  617. * 2 bits will be used.
  618. *
  619. **/
  620. public virtual void write_ref(System.String node, int[] ids, int creation)
  621. {
  622. int arity = (int) (ids.Length);
  623. if (arity > 3)
  624. arity = 3;
  625. // max 3 words in ref
  626. if (arity == 1)
  627. {
  628. // use old method
  629. this.write_ref(node, ids[0], creation);
  630. }
  631. else
  632. {
  633. // r6 ref
  634. this.write1(OtpExternal.newRefTag);
  635. // how many id values
  636. this.write2BE(arity);
  637. this.write_atom(node);
  638. // note: creation BEFORE id in r6 ref
  639. this.write1(creation & 0x3); // 2 bits
  640. // first int gets truncated to 18 bits
  641. this.write4BE(ids[0] & 0x3ffff);
  642. // remaining ones are left as is
  643. for (int i = 1; i < arity; i++)
  644. this.write4BE(ids[i]);
  645. }
  646. }
  647. /*
  648. * Write a string to the stream.
  649. *
  650. * @param s the string to write.
  651. **/
  652. public virtual void write_string(System.String s)
  653. {
  654. int len = s.Length;
  655. switch (len)
  656. {
  657. case 0:
  658. this.write_nil();
  659. break;
  660. default:
  661. //UPGRADE_NOTE: This code will be optimized in the future;
  662. byte[] tmpBytes;
  663. int i;
  664. string tmpStr;
  665. tmpStr = s;
  666. tmpBytes = new byte[tmpStr.Length];
  667. i = 0;
  668. while (i < tmpStr.Length)
  669. {
  670. tmpBytes[i] = (byte) tmpStr[i];
  671. i++;
  672. }
  673. byte[] bytebuf = tmpBytes;
  674. /*switch to se if the length of
  675. the byte array is equal to the
  676. length of the list */
  677. if (bytebuf.Length == len)
  678. {
  679. /*Usual */
  680. this.write1(OtpExternal.stringTag);
  681. this.write2BE(len);
  682. this.writeN(bytebuf);
  683. }
  684. else
  685. {
  686. /*Unicode */
  687. char[] charbuf = s.ToCharArray();
  688. this.write_list_head(len);
  689. for (int i2 = 0; i2 < len; i2++)
  690. this.write_char(charbuf[i2]);
  691. this.write_nil();
  692. }
  693. break;
  694. }
  695. }
  696. /*
  697. This does not work when char > 1 byte Unicode is used
  698. public void write_string(String s) {
  699. this.write1(OtpExternal.stringTag);
  700. this.write2BE(s.length());
  701. this.writeN(s.getBytes());
  702. }*/
  703. /*
  704. * Write an arbitrary Erlang term to the stream.
  705. *
  706. * @param o the Erlang term to write.
  707. */
  708. public virtual void write_any(Erlang.Object o)
  709. {
  710. // calls one of the above functions, depending on o
  711. o.encode(this);
  712. }
  713. }
  714. }