PageRenderTime 28ms CodeModel.GetById 2ms app.highlight 20ms RepoModel.GetById 2ms app.codeStats 0ms

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

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