PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Otp/OtpOutputStream.cs

https://github.com/saleyn/otp.net
C# | 918 lines | 411 code | 70 blank | 437 comment | 61 complexity | 72b4fe710de395f44351a7737a057d83 MD5 | raw file
  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 serialized Erlang term.
  55. **/
  56. public OtpOutputStream(Erlang.Object o):this(o, false, false) {}
  57. /*
  58. * Create a stream containing the serialized Erlang term.
  59. * Optionally include in the beginning Erlang protocol version byte.
  60. **/
  61. public OtpOutputStream(Erlang.Object o, bool writeVersion)
  62. : this(o, writeVersion, false) {}
  63. /*
  64. * Create a stream containing the serialized Erlang term.
  65. * Optionally include in the beginning Erlang protocol version byte.
  66. **/
  67. public OtpOutputStream(Erlang.Object o, bool writeVersion, bool writePktSize)
  68. : this()
  69. {
  70. if (!writePktSize)
  71. {
  72. encodeObject(o, writeVersion);
  73. return;
  74. }
  75. write4BE(0); // make space for length data,
  76. // but final value is not yet known
  77. encodeObject(o, writeVersion);
  78. poke4BE(0, this._count - 4);
  79. }
  80. private void encodeObject(Erlang.Object o, bool writeVersion)
  81. {
  82. if (writeVersion)
  83. this.write(OtpExternal.versionTag);
  84. this.write_any(o);
  85. }
  86. // package scope
  87. /*
  88. * Get the contents of the output stream as an input stream instead.
  89. * This is used internally in {@link OtpCconnection} for tracing
  90. * outgoing packages.
  91. *
  92. * @param offset where in the output stream to read data from when
  93. * creating the input stream. The offset is necessary because header
  94. * contents start 5 bytes into the header buffer, whereas payload
  95. * contents start at the beginning
  96. *
  97. * @return an input stream containing the same raw data.
  98. **/
  99. internal virtual OtpInputStream getOtpInputStream(int offset)
  100. {
  101. return new OtpInputStream(buf, offset, _count - offset);
  102. }
  103. public Erlang.Binary ToBinary()
  104. {
  105. byte[] tmp = new byte[_count];
  106. Array.Copy(buf, 0, tmp, 0, _count);
  107. return new Otp.Erlang.Binary(tmp);
  108. }
  109. /*
  110. * Reset the stream so that it can be reused.
  111. **/
  112. public virtual void reset()
  113. {
  114. _count = 0;
  115. }
  116. /*
  117. * Get internal buffer
  118. **/
  119. public byte[] getBuffer() { return buf; }
  120. /*
  121. * Get the current capacity of the stream. As bytes are added the
  122. * capacity of the stream is increased automatically, however this
  123. * method returns the current size.
  124. *
  125. * @return the size of the internal buffer used by the stream.
  126. **/
  127. public int size()
  128. {
  129. return _size;
  130. }
  131. /*
  132. * Get the current position in the stream.
  133. *
  134. * @return the current position in the stream.
  135. **/
  136. public int getPos()
  137. {
  138. return _count;
  139. }
  140. /*
  141. * Get the number of bytes in the stream.
  142. *
  143. * @return the number of bytes in the stream.
  144. **/
  145. public int count()
  146. {
  147. return _count;
  148. }
  149. /*
  150. * Write one byte to the stream.
  151. *
  152. * @param b the byte to write.
  153. *
  154. **/
  155. public virtual void write(byte b)
  156. {
  157. if (_count >= _size)
  158. {
  159. // System.err.println("Expanding buffer from " + size + " to " + size + defaultIncrement);
  160. byte[] tmp = new byte[_size + defaultIncrement];
  161. Array.Copy(buf, 0, tmp, 0, _count);
  162. _size += defaultIncrement;
  163. buf = tmp;
  164. }
  165. buf[_count++] = b;
  166. }
  167. /*
  168. * Write an array of bytes to the stream.
  169. *
  170. * @param buf the array of bytes to write.
  171. *
  172. **/
  173. public virtual void write(byte[] buf)
  174. {
  175. if (_count + buf.Length > _size)
  176. {
  177. // System.err.println("Expanding buffer from " +
  178. // size + " to " + buf.length + size + defaultIncrement);
  179. byte[] tmp = new byte[_size + buf.Length + defaultIncrement];
  180. Array.Copy(this.buf, 0, tmp, 0, _count);
  181. _size += defaultIncrement + buf.Length;
  182. this.buf = tmp;
  183. }
  184. Array.Copy(buf, 0, this.buf, _count, buf.Length);
  185. _count += (int) (buf.Length);
  186. }
  187. /*
  188. * Write the contents of the stream to an OutputStream.
  189. *
  190. * @param os the OutputStream to write to.
  191. *
  192. * @exception C#.io.IOException if there is an error writing to
  193. * the OutputStream.
  194. **/
  195. public virtual void writeTo(System.IO.Stream os)
  196. {
  197. os.Write(buf, 0, _count);
  198. os.Flush();
  199. }
  200. public void writeTo(System.Net.Sockets.Socket sock)
  201. {
  202. sock.Send(buf, _count, System.Net.Sockets.SocketFlags.None);
  203. }
  204. /*
  205. * Write the low byte of a value to the stream.
  206. *
  207. * @param n the value to use.
  208. *
  209. **/
  210. public virtual void write1(long n)
  211. {
  212. write((byte) (n & 0xff));
  213. }
  214. /*
  215. * Write an array of bytes to the stream.
  216. *
  217. * @param buf the array of bytes to write.
  218. *
  219. **/
  220. public virtual void writeN(byte[] bytes)
  221. {
  222. write(bytes);
  223. }
  224. /*
  225. * Write the low two bytes of a value to the stream in big endian
  226. * order.
  227. *
  228. * @param n the value to use.
  229. **/
  230. public virtual void write2BE(long n)
  231. {
  232. write((byte) ((n & 0xff00) >> 8));
  233. write((byte) (n & 0xff));
  234. }
  235. /*
  236. * Write the low four bytes of a value to the stream in big endian
  237. * order.
  238. *
  239. * @param n the value to use.
  240. **/
  241. public virtual void write4BE(long n)
  242. {
  243. write((byte) ((n & 0xff000000) >> 24));
  244. write((byte) ((n & 0xff0000) >> 16));
  245. write((byte) ((n & 0xff00) >> 8));
  246. write((byte) (n & 0xff));
  247. }
  248. /*
  249. * Write the low two bytes of a value to the stream in little endian
  250. * order.
  251. *
  252. * @param n the value to use.
  253. **/
  254. public virtual void write2LE(long n)
  255. {
  256. write((byte) (n & 0xff));
  257. write((byte) ((n & 0xff00) >> 8));
  258. }
  259. /*
  260. * Write the low four bytes of a value to the stream in little
  261. * endian order.
  262. *
  263. * @param n the value to use.
  264. **/
  265. public virtual void write4LE(long n)
  266. {
  267. write((byte) (n & 0xff));
  268. write((byte) ((n & 0xff00) >> 8));
  269. write((byte) ((n & 0xff0000) >> 16));
  270. write((byte) ((n & 0xff000000) >> 24));
  271. }
  272. /*
  273. * Write the low four bytes of a value to the stream in bif endian
  274. * order, at the specified position. If the position specified is
  275. * beyond the end of the stream, this method will have no effect.
  276. *
  277. * Normally this method should be used in conjunction with {@link
  278. * #getPos() getPos()}, when is is necessary to insert data into the
  279. * stream before it is known what the actual value should be. For
  280. * example:
  281. *
  282. <pre>
  283. int pos = s.getPos();
  284. s.write4BE(0); // make space for length data,
  285. // but final value is not yet known
  286. [ ...more write statements...]
  287. // later... when we know the length value
  288. s.poke4BE(pos, length);
  289. </pre>
  290. *
  291. * @param offset the position in the stream.
  292. * @param n the value to use.
  293. **/
  294. public virtual void poke4BE(int offset, int n)
  295. {
  296. if (offset < _count)
  297. {
  298. buf[offset + 0] = ((byte) ((n & 0xff000000) >> 24));
  299. buf[offset + 1] = ((byte) ((n & 0xff0000) >> 16));
  300. buf[offset + 2] = ((byte) ((n & 0xff00) >> 8));
  301. buf[offset + 3] = ((byte) (n & 0xff));
  302. }
  303. }
  304. /*
  305. * Write a string to the stream as an Erlang atom.
  306. *
  307. * @param atom the string to write.
  308. **/
  309. public virtual void write_atom(System.String atom)
  310. {
  311. this.write1(OtpExternal.atomTag);
  312. this.write2BE(atom.Length);
  313. //UPGRADE_NOTE: This code will be optimized in the future;
  314. byte[] tmpBytes;
  315. int i;
  316. string tmpStr;
  317. tmpStr = atom;
  318. tmpBytes = new byte[tmpStr.Length];
  319. i = 0;
  320. while (i < tmpStr.Length)
  321. {
  322. tmpBytes[i] = (byte) tmpStr[i];
  323. i++;
  324. }
  325. this.writeN(tmpBytes);
  326. }
  327. public int encode_size(Erlang.Object o)
  328. {
  329. if (o is Erlang.Atom) return 1 + 2 + o.atomValue().Length;
  330. else if (o is Erlang.Boolean) return 1 + 2 + (o.boolValue()
  331. ? Erlang.Boolean.s_true.atomValue().Length
  332. : Erlang.Boolean.s_false.atomValue().Length);
  333. else if (o is Erlang.Binary) return 5 + o.binaryValue().Length;
  334. else if (o is Erlang.Long)
  335. {
  336. long l = o.longValue();
  337. if ((l & 0xff) == l) return 2;
  338. else if ((l <= OtpExternal.erlMax) && (l >= OtpExternal.erlMin)) return 5;
  339. return long_arity(l);
  340. }
  341. else if (o is Erlang.Byte) return 1 + 1;
  342. else if (o is Erlang.Double) return 9;
  343. else if (o is Erlang.String)
  344. {
  345. string l = o.stringValue();
  346. if (l.Length == 0) return 1;
  347. if (l.Length < 0xffff) return 2 + l.Length;
  348. return 1 + 4 + 2 * l.Length;
  349. }
  350. else if (o is Erlang.List)
  351. {
  352. Erlang.List l = o.listValue();
  353. if (l.arity() == 0)
  354. return 1;
  355. int sz = 5;
  356. for (int i = 0; i < l.arity(); i++)
  357. sz += encode_size(l[i]);
  358. return sz;
  359. }
  360. else if (o is Erlang.Tuple)
  361. {
  362. Erlang.Tuple l = o.tupleValue();
  363. int sz = 1 + (l.arity() < 0xff ? 1 : 4);
  364. for (int i = 0; i < l.arity(); i++)
  365. sz += encode_size(l[i]);
  366. return sz;
  367. }
  368. else if (o is Erlang.Pid)
  369. {
  370. Erlang.Pid p = o.pidValue();
  371. return 1 + (1 + 2 + p.node().Length) + 4 + 4 + 1;
  372. }
  373. else if (o is Erlang.Ref)
  374. {
  375. Erlang.Ref p = o.refValue();
  376. int[] ids = p.ids();
  377. return 1 + (1 + 2 + p.node().Length) + 1 + 4 * ids.Length;
  378. }
  379. else if (o is Erlang.Port)
  380. {
  381. Erlang.Port p = o.portValue();
  382. return 1 + (1 + 2 + p.node().Length) + 4 + 1;
  383. }
  384. else
  385. throw new Erlang.Exception("Unknown encode size for object: " + o.ToString());
  386. }
  387. /*
  388. * Write an array of bytes to the stream as an Erlang binary.
  389. *
  390. * @param bin the array of bytes to write.
  391. **/
  392. public virtual void write_binary(byte[] bin)
  393. {
  394. this.write1(OtpExternal.binTag);
  395. this.write4BE(bin.Length);
  396. this.writeN(bin);
  397. }
  398. /*
  399. * Write a boolean value to the stream as the Erlang atom 'true' or
  400. * 'false'.
  401. *
  402. * @param b the boolean value to write.
  403. **/
  404. public virtual void write_boolean(bool b)
  405. {
  406. this.write_atom((b ? Erlang.Boolean.s_true : Erlang.Boolean.s_false).atomValue());
  407. }
  408. /*
  409. * Write a single byte to the stream as an Erlang integer.
  410. *
  411. * @param b the byte to use.
  412. **/
  413. public virtual void write_byte(byte b)
  414. {
  415. this.write_long(b, false);
  416. }
  417. /*
  418. * Write a character to the stream as an Erlang integer.
  419. *
  420. * @param c the character to use.
  421. **/
  422. public virtual void write_char(char c)
  423. {
  424. this.write_long(c, false);
  425. }
  426. /*
  427. * Write a double value to the stream.
  428. *
  429. * @param d the double to use.
  430. **/
  431. public virtual void write_double(double d)
  432. {
  433. this.write1(OtpExternal.newFloatTag);
  434. byte[] data = BitConverter.GetBytes(d);
  435. if (BitConverter.IsLittleEndian)
  436. {
  437. Array.Reverse(data);
  438. }
  439. this.write(data);
  440. /*
  441. double val;
  442. int exp = 0;
  443. int sign = 0;
  444. System.String str;
  445. // remove sign to simplify decimal shift
  446. if (d >= 0)
  447. {
  448. val = d;
  449. }
  450. else
  451. {
  452. sign = 1;
  453. val = - d;
  454. }
  455. // move the decimal point until we have a single units digit
  456. if (System.Math.Sign(val) != 0)
  457. {
  458. // until greater than or equal to 1.0 -> multiply by 10
  459. while (val < 1.0)
  460. {
  461. val *= 10;
  462. exp--;
  463. }
  464. // until strictly less than 10.0 -> divide by 10
  465. while (val >= 10.0)
  466. {
  467. val /= 10;
  468. exp++;
  469. }
  470. }
  471. // get 20 decimal digits, put sign back, add new exponent
  472. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.ROUND_HALF_EVEN will be considered in a future release.;
  473. //val = val.setScale(20, BigDecimal.ROUND_HALF_EVEN);
  474. //UPGRADE_TODO: The equivalent in .NET for Class C#.math.BigDecimal.toString will be considered in a future release.;
  475. str = (sign == 1?"-":"") + System.Convert.ToString(val) + "e" + System.Convert.ToString(exp);
  476. // write the value
  477. this.write1(OtpExternal.floatTag);
  478. //UPGRADE_NOTE: This code will be optimized in the future;
  479. byte[] tmpBytes;
  480. int i;
  481. string tmpStr;
  482. tmpStr = str;
  483. tmpBytes = new byte[tmpStr.Length];
  484. i = 0;
  485. while (i < tmpStr.Length)
  486. {
  487. tmpBytes[i] = (byte) tmpStr[i];
  488. i++;
  489. }
  490. this.writeN(tmpBytes);
  491. // pad with zeros to 31 bytes
  492. //UPGRADE_NOTE: This code will be optimized in the future;
  493. byte[] tmpBytes2;
  494. int i2;
  495. string tmpStr2;
  496. tmpStr2 = str;
  497. tmpBytes2 = new byte[tmpStr2.Length];
  498. i2 = 0;
  499. while (i2 < tmpStr2.Length)
  500. {
  501. tmpBytes2[i2] = (byte) tmpStr2[i2];
  502. i2++;
  503. }
  504. int i3 = (int) (tmpBytes2.Length);
  505. for (; i3 < 31; i3++)
  506. this.write1(0);
  507. */
  508. }
  509. /*
  510. * Write a float value to the stream.
  511. *
  512. * @param f the float to use.
  513. **/
  514. public virtual void write_float(float f)
  515. {
  516. this.write_double(f);
  517. }
  518. /*
  519. * Write a long to the stream.
  520. *
  521. * @param l the long to use.
  522. **/
  523. public void write_long(long l) { write_long(l, l < 0); }
  524. private void write_long(long l, bool isNegative)
  525. {
  526. if ((l & 0xff) == l)
  527. {
  528. // will fit in one byte
  529. this.write1(OtpExternal.smallIntTag);
  530. this.write1(l);
  531. }
  532. else if ((l <= OtpExternal.erlMax) && (l >= OtpExternal.erlMin))
  533. {
  534. this.write1(OtpExternal.intTag);
  535. this.write4BE(l);
  536. }
  537. else
  538. {
  539. int neg = isNegative ? 1 : 0;
  540. ulong v = (ulong)(isNegative ? -l : l);
  541. byte arity = 0;
  542. this.write1(OtpExternal.smallBigTag);
  543. int arity_pos = _count;
  544. this.write1(0); // Fill in later
  545. this.write1(neg); // sign
  546. while (v != 0) {
  547. this.write1((byte)(v & 0xff)); // write lowest byte
  548. v >>= 8; // shift unsigned
  549. arity++;
  550. }
  551. buf[arity_pos] = arity;
  552. }
  553. }
  554. static public int long_arity(long l)
  555. {
  556. ulong v = (ulong)(l < 0 ? -l : l);
  557. int sz = 3; /* Type, arity and sign */
  558. while (v != 0) { sz++; v >>= 8; }
  559. return sz;
  560. }
  561. /*
  562. * Write a positive long to the stream.
  563. *
  564. * @param ul the long to use.
  565. **/
  566. public virtual void write_ulong(ulong ul)
  567. {
  568. this.write_long((long)ul, false);
  569. }
  570. /*
  571. * Write an integer to the stream.
  572. *
  573. * @param i the integer to use.
  574. **/
  575. public virtual void write_int(int i)
  576. {
  577. this.write_long(i, i < 0);
  578. }
  579. /*
  580. * Write a positive integer to the stream.
  581. *
  582. * @param ui the integer to use.
  583. **/
  584. public virtual void write_uint(uint ui)
  585. {
  586. this.write_long(ui, false);
  587. }
  588. /*
  589. * Write a short to the stream.
  590. *
  591. * @param s the short to use.
  592. **/
  593. public virtual void write_short(short s)
  594. {
  595. this.write_long(s, false);
  596. }
  597. /*
  598. * Write a positive short to the stream.
  599. *
  600. * @param s the short to use.
  601. **/
  602. public virtual void write_ushort(ushort us)
  603. {
  604. this.write_long(us, false);
  605. }
  606. /*
  607. * Write an Erlang list header to the stream. After calling this
  608. * method, you must write 'arity' elements to the stream followed by
  609. * nil, or it will not be possible to decode it later.
  610. *
  611. * @param arity the number of elements in the list.
  612. **/
  613. public virtual void write_list_head(int arity)
  614. {
  615. if (arity == 0)
  616. {
  617. this.write_nil();
  618. }
  619. else
  620. {
  621. this.write1(OtpExternal.listTag);
  622. this.write4BE(arity);
  623. }
  624. }
  625. /*
  626. * Write an empty Erlang list to the stream.
  627. **/
  628. public virtual void write_nil()
  629. {
  630. this.write1(OtpExternal.nilTag);
  631. }
  632. /*
  633. * Write an Erlang tuple header to the stream. After calling this
  634. * method, you must write 'arity' elements to the stream or it will
  635. * not be possible to decode it later.
  636. *
  637. * @param arity the number of elements in the tuple.
  638. **/
  639. public virtual void write_tuple_head(int arity)
  640. {
  641. if (arity < 0xff)
  642. {
  643. this.write1(OtpExternal.smallTupleTag);
  644. this.write1(arity);
  645. }
  646. else
  647. {
  648. this.write1(OtpExternal.largeTupleTag);
  649. this.write4BE(arity);
  650. }
  651. }
  652. /*
  653. * Write an Erlang PID to the stream.
  654. *
  655. * @param node the nodename.
  656. *
  657. * @param id an arbitrary number. Only the low order 15 bits will
  658. * be used.
  659. *
  660. * @param serial another arbitrary number. Only the low order 3 bits
  661. * will be used.
  662. *
  663. * @param creation yet another arbitrary number. Only the low order
  664. * 2 bits will be used.
  665. *
  666. **/
  667. public virtual void write_pid(System.String node, int id, int serial, int creation)
  668. {
  669. this.write1(OtpExternal.pidTag);
  670. this.write_atom(node);
  671. this.write4BE(id & 0x7fff); // 15 bits
  672. this.write4BE(serial & 0x7); // 3 bits
  673. this.write1(creation & 0x3); // 2 bits
  674. }
  675. /*
  676. * Write an Erlang port to the stream.
  677. *
  678. * @param node the nodename.
  679. *
  680. * @param id an arbitrary number. Only the low order 18 bits will
  681. * be used.
  682. *
  683. * @param creation another arbitrary number. Only the low order 2
  684. * bits will be used.
  685. *
  686. **/
  687. public virtual void write_port(System.String node, int id, int creation)
  688. {
  689. this.write1(OtpExternal.portTag);
  690. this.write_atom(node);
  691. this.write4BE(id & 0x3ffff); // 18 bits
  692. this.write1(creation & 0x3); // 2 bits
  693. }
  694. /*
  695. * Write an old style Erlang ref to the stream.
  696. *
  697. * @param node the nodename.
  698. *
  699. * @param id an arbitrary number. Only the low order 18 bits will
  700. * be used.
  701. *
  702. * @param creation another arbitrary number. Only the low order 2
  703. * bits will be used.
  704. *
  705. **/
  706. public virtual void write_ref(System.String node, int id, int creation)
  707. {
  708. this.write1(OtpExternal.refTag);
  709. this.write_atom(node);
  710. this.write4BE(id & 0x3ffff); // 18 bits
  711. this.write1(creation & 0x3); // 2 bits
  712. }
  713. /*
  714. * Write a new style (R6 and later) Erlang ref to the stream.
  715. *
  716. * @param node the nodename.
  717. *
  718. * @param ids an array of arbitrary numbers. Only the low order 18
  719. * bits of the first number will be used. If the array contains only
  720. * one number, an old style ref will be written instead. At most
  721. * three numbers will be read from the array.
  722. *
  723. * @param creation another arbitrary number. Only the low order
  724. * 2 bits will be used.
  725. *
  726. **/
  727. public virtual void write_ref(System.String node, int[] ids, int creation)
  728. {
  729. int arity = (int) (ids.Length);
  730. if (arity > 3)
  731. arity = 3;
  732. // max 3 words in ref
  733. if (arity == 1)
  734. {
  735. // use old method
  736. this.write_ref(node, ids[0], creation);
  737. }
  738. else
  739. {
  740. // r6 ref
  741. this.write1(OtpExternal.newRefTag);
  742. // how many id values
  743. this.write2BE(arity);
  744. this.write_atom(node);
  745. // note: creation BEFORE id in r6 ref
  746. this.write1(creation & 0x3); // 2 bits
  747. // first int gets truncated to 18 bits
  748. this.write4BE(ids[0] & 0x3ffff);
  749. // remaining ones are left as is
  750. for (int i = 1; i < arity; i++)
  751. this.write4BE(ids[i]);
  752. }
  753. }
  754. /*
  755. * Write a string to the stream.
  756. *
  757. * @param s the string to write.
  758. **/
  759. public virtual void write_string(System.String s)
  760. {
  761. int len = s.Length;
  762. switch (len)
  763. {
  764. case 0:
  765. this.write_nil();
  766. break;
  767. default:
  768. System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  769. Byte[] bytebuf = encoding.GetBytes(s);
  770. if (bytebuf.Length < 0xffff)
  771. {
  772. this.write1(OtpExternal.stringTag);
  773. this.write2BE(len);
  774. this.writeN(bytebuf);
  775. }
  776. else
  777. {
  778. this.write_list_head(len);
  779. for (int i2 = 0; i2 < len; i2++)
  780. this.write_byte(bytebuf[i2]);
  781. this.write_nil();
  782. }
  783. /*
  784. //UPGRADE_NOTE: This code will be optimized in the future;
  785. byte[] tmpBytes;
  786. int i;
  787. string tmpStr;
  788. tmpStr = s;
  789. tmpBytes = new byte[tmpStr.Length];
  790. i = 0;
  791. while (i < tmpStr.Length)
  792. {
  793. tmpBytes[i] = (byte) tmpStr[i];
  794. i++;
  795. }
  796. byte[] bytebuf = tmpBytes;
  797. if (bytebuf.Length == len)
  798. {
  799. // Usual
  800. this.write1(OtpExternal.stringTag);
  801. this.write2BE(len);
  802. this.writeN(bytebuf);
  803. }
  804. else
  805. {
  806. // Unicode
  807. char[] charbuf = s.ToCharArray();
  808. this.write_list_head(len);
  809. for (int i2 = 0; i2 < len; i2++)
  810. this.write_char(charbuf[i2]);
  811. this.write_nil();
  812. }
  813. */
  814. break;
  815. }
  816. }
  817. /*
  818. This does not work when char > 1 byte Unicode is used
  819. public void write_string(String s) {
  820. this.write1(OtpExternal.stringTag);
  821. this.write2BE(s.length());
  822. this.writeN(s.getBytes());
  823. }*/
  824. /*
  825. * Write an arbitrary Erlang term to the stream.
  826. *
  827. * @param o the Erlang term to write.
  828. */
  829. public virtual void write_any(Erlang.Object o)
  830. {
  831. // calls one of the above functions, depending on o
  832. o.encode(this);
  833. }
  834. }
  835. }