context: /***************************************************************************
   *                              PacketWriter.cs
   *                            -------------------
   *   begin                : May 1, 2002
   *   copyright            : (C) The RunUO Software Team
   *   email                : info@runuo.com
   *
   *   $Id$
   *
   ***************************************************************************/
  
  /***************************************************************************
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   ***************************************************************************/
  
  using System;
  using System.IO;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;
  
  namespace Server.Network
  {
      /// <summary>
      /// Provides functionality for writing primitive binary data.
      /// </summary>
      public class PacketWriter
      {
  	private static Stack<PacketWriter> m_Pool = new Stack<PacketWriter>();
  
  	public static PacketWriter CreateInstance()
  	{
  	    return CreateInstance( 32 );
  	}
  
  	public static PacketWriter CreateInstance( int capacity )
  	{
  	    PacketWriter pw = null;
  
  	    lock ( m_Pool )
  	    {
  		if ( m_Pool.Count > 0 )
  		{
  		    pw = m_Pool.Pop();
  
  		    if ( pw != null )
  		    {
  			pw.m_Capacity = capacity;
  			pw.m_Stream.SetLength( 0 );
  		    }
  		}
  	    }
  
  	    if ( pw == null )
  		pw = new PacketWriter( capacity );
  
  	    return pw;
  	}
  
  	public static void ReleaseInstance( PacketWriter pw )
  	{
  	    lock ( m_Pool )
  	    {
  		if ( !m_Pool.Contains( pw ) )
  		{
  		    m_Pool.Push( pw );
  		}
  		else
  		{
  		    try
  		    {
  			using ( StreamWriter op = new StreamWriter( new FileStream("neterr.log", FileMode.Append )))
  				{
  				    op.WriteLine("{0}\tInstance pool contains writer", DateTime.UtcNow);
  				}
  				}
  			catch
  			{
  			    Console.WriteLine( "net error" );
  			}
  		    }
  		}
  	    }
  
  	    /// <summary>
  	    /// Internal stream which holds the entire packet.
  	    /// </summary>
  	    private MemoryStream m_Stream;
  
  	    private int m_Capacity;
  
  	    /// <summary>
  	    /// Internal format buffer.
  	    /// </summary>
  	    private byte[] m_Buffer = new byte[4];
  
  	    /// <summary>
  	    /// Instantiates a new PacketWriter instance with the default capacity of 4 bytes.
  	    /// </summary>
  	    public PacketWriter() : this( 32 )
  	    {
  	    }
  
  	    /// <summary>
  	    /// Instantiates a new PacketWriter instance with a given capacity.
  	    /// </summary>
  	    /// <param name="capacity">Initial capacity for the internal stream.</param>
  	    public PacketWriter( int capacity )
  	    {
  		m_Stream = new MemoryStream( capacity );
  		m_Capacity = capacity;
  	    }
  
  	    /// <summary>
  	    /// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
  	    /// </summary>
  	    public void Write( bool value )
  	    {
  		m_Stream.WriteByte( (byte)(value ? 1 : 0) );
  	    }
  
  	    /// <summary>
  	    /// Writes a 1-byte unsigned integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( byte value )
  	    {
  		m_Stream.WriteByte( value );
  	    }
  
  	    /// <summary>
  	    /// Writes a 1-byte signed integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( sbyte value )
  	    {
  		m_Stream.WriteByte( (byte) value );
  	    }
  
  	    /// <summary>
  	    /// Writes a 2-byte signed integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( short value )
  	    {
  		m_Buffer[0] = (byte)(value >> 8);
  		m_Buffer[1] = (byte) value;
  
  		m_Stream.Write( m_Buffer, 0, 2 );
  	    }
  
  	    /// <summary>
  	    /// Writes a 2-byte unsigned integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( ushort value )
  	    {
  		m_Buffer[0] = (byte)(value >> 8);
  		m_Buffer[1] = (byte) value;
  
  		m_Stream.Write( m_Buffer, 0, 2 );
  	    }
  
  	    /// <summary>
  	    /// Writes a 4-byte signed integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( int value )
  	    {
  		m_Buffer[0] = (byte)(value >> 24);
  		m_Buffer[1] = (byte)(value >> 16);
  		m_Buffer[2] = (byte)(value >>  8);
  		m_Buffer[3] = (byte) value;
  
  		m_Stream.Write( m_Buffer, 0, 4 );
  	    }
  
  	    /// <summary>
  	    /// Writes a 4-byte unsigned integer value to the underlying stream.
  	    /// </summary>
  	    public void Write( uint value )
  	    {
  		m_Buffer[0] = (byte)(value >> 24);
  		m_Buffer[1] = (byte)(value >> 16);
  		m_Buffer[2] = (byte)(value >>  8);
  		m_Buffer[3] = (byte) value;
  
  		m_Stream.Write( m_Buffer, 0, 4 );
  	    }
  
  	    /// <summary>
  	    /// Writes a sequence of bytes to the underlying stream
  	    /// </summary>
  	    public void Write( byte[] buffer, int offset, int size )
  	    {
  		m_Stream.Write( buffer, offset, size );
  	    }
  
  	    /// <summary>
  	    /// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
  	    /// </summary>
  	    public void WriteAsciiFixed( string value, int size )
  	    {
  		if ( value == null )
  		{
  		    Console.WriteLine( "Network: Attempted to WriteAsciiFixed() with null value" );
  		    value = String.Empty;
  		}
  
  		int length = value.Length;
  
  		m_Stream.SetLength( m_Stream.Length + size );
  
  		ArraySegment<byte> segment;
  		byte[] buf;
  		
  		if( !m_Stream.TryGetBuffer( out segment ) ){
  		    throw new Exception("Unable to get buffer!");
  		}
  
  		buf = segment.Array;
  
  		if ( length >= size )
  		    m_Stream.Position += Encoding.ASCII.GetBytes( value, 0, size, buf, (int)m_Stream.Position );
  		else
  		{
  		    Encoding.ASCII.GetBytes( value, 0, length, buf, (int)m_Stream.Position );
  		    m_Stream.Position += size;
  		}
  
  		/*byte[] buffer = Encoding.ASCII.GetBytes( value );
  
  		  if ( buffer.Length >= size )
  		  {
  		  m_Stream.Write( buffer, 0, size );
  		  }
  		  else
  		  {
  		  m_Stream.Write( buffer, 0, buffer.Length );
  		  Fill( size - buffer.Length );
  		  }*/
  	    }
  
  	    /// <summary>
  	    /// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
  	    /// </summary>
  	    public void WriteAsciiNull( string value )
  	    {
  		if ( value == null )
  		{
  		    Console.WriteLine( "Network: Attempted to WriteAsciiNull() with null value" );
  		    value = String.Empty;
  		}
  
  		int length = value.Length;
  
  		m_Stream.SetLength( m_Stream.Length + length + 1 );
  
  		Encoding.ASCII.GetBytes( value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position );
  		m_Stream.Position += length + 1;
  
  		/*byte[] buffer = Encoding.ASCII.GetBytes( value );
  
  		  m_Stream.Write( buffer, 0, buffer.Length );
  		  m_Stream.WriteByte( 0 );*/
  	    }
  
  	    /// <summary>
  	    /// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character.
  	    /// </summary>
  	    public void WriteLittleUniNull( string value )
  	    {
  		if ( value == null )
  		{
  		    Console.WriteLine( "Network: Attempted to WriteLittleUniNull() with null value" );
  		    value = String.Empty;
  		}
  
  		int length = value.Length;
  
  		m_Stream.SetLength( m_Stream.Length + ( ( length + 1 ) * 2 ) );
  
  		ArraySegment<byte> segment;
  		byte[] buf;
  		
  		if( !m_Stream.TryGetBuffer( out segment ) ){
  		    throw new Exception("Unable to get buffer!");
  		}
  
  		buf = segment.Array; //sure hope the array segment covers the entire underlying array --sith
  		
  		m_Stream.Position += Encoding.Unicode.GetBytes( value, 0, length, buf, (int)m_Stream.Position );
  		m_Stream.Position += 2;
  
  		/*byte[] buffer = Encoding.Unicode.GetBytes( value );
  
  		  m_Stream.Write( buffer, 0, buffer.Length );
  
  		  m_Buffer[0] = 0;
  		  m_Buffer[1] = 0;
  		  m_Stream.Write( m_Buffer, 0, 2 );*/
  	    }
  
  	    /// <summary>
  	    /// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
  	    /// </summary>
  	    public void WriteLittleUniFixed( string value, int size )
  	    {
  		if ( value == null )
  		{
  		    Console.WriteLine( "Network: Attempted to WriteLittleUniFixed() with null value" );
  		    value = String.Empty;
  		}
  
  		size *= 2;
  
  		int length = value.Length;
  
  		m_Stream.SetLength( m_Stream.Length + size );
  
  		ArraySegment<byte> segment;
  		byte[] buf;
  		
  		if( !m_Stream.TryGetBuffer( out segment ) ){
  		    throw new Exception("Unable to get buffer!");
  		}
  
  		buf = segment.Array;
  
  		if ( ( length * 2 ) >= size )
  		    m_Stream.Position += Encoding.Unicode.GetBytes( value, 0, length, buf, (int)m_Stream.Position );
  		else
  		{
  		    Encoding.Unicode.GetBytes( value, 0, length, buf, (int)m_Stream.Position );
  		    m_Stream.Position += size;
  		}
  
  		/*size *= 2;
  
  		  byte[] buffer = Encoding.Unicode.GetBytes( value );
  
  		  if ( buffer.Length >= size )
  		  {
  		  m_Stream.Write( buffer, 0, size );
  		  }
  		  else
  		  {
  
gt: 		  m_Stream.Write( buffer, 0, buffer.Length );