<commit_before>﻿#region -- License Terms --
//
// MessagePack for CLI
//
// Copyright (C) 2010 FUJIWARA, Yusuke
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//
#endregion -- License Terms --

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

namespace MsgPack
{
	partial struct MessagePackObject
#if !SILVERLIGHT
 : ISerializable
#endif
	{
		#region -- Type Code Constants --

		private static readonly ValueTypeCode _sbyteTypeCode = new ValueTypeCode( typeof( sbyte ) );
		private static readonly ValueTypeCode _byteTypeCode = new ValueTypeCode( typeof( byte ) );
		private static readonly ValueTypeCode _int16TypeCode = new ValueTypeCode( typeof( short ) );
		private static readonly ValueTypeCode _uint16TypeCode = new ValueTypeCode( typeof( ushort ) );
		private static readonly ValueTypeCode _int32TypeCode = new ValueTypeCode( typeof( int ) );
		private static readonly ValueTypeCode _uint32TypeCode = new ValueTypeCode( typeof( uint ) );
		private static readonly ValueTypeCode _int64TypeCode = new ValueTypeCode( typeof( long ) );
		private static readonly ValueTypeCode _uint64TypeCode = new ValueTypeCode( typeof( ulong ) );
		private static readonly ValueTypeCode _singleTypeCode = new ValueTypeCode( typeof( float ) );
		private static readonly ValueTypeCode _doubleTypeCode = new ValueTypeCode( typeof( double ) );
		private static readonly ValueTypeCode _booleanTypeCode = new ValueTypeCode( typeof( bool ) );

		#endregion -- Type Code Constants --

		/// <summary>
		///		Instance represents nil. This is equal to default value.
		/// </summary>
		public static readonly MessagePackObject Nil = new MessagePackObject();

		#region -- Type Code Fields & Properties --

		private readonly object _handleOrTypeCode;

		/// <summary>
		///		Get whether this instance represents nil.
		/// </summary>
		/// <value>If this instance represents nil object, then true.</value>
		public bool IsNil
		{
			get { return this._handleOrTypeCode == null; }
		}

		private readonly ulong _value;

		#endregion -- Type Code Fields & Properties --

		#region -- Constructors --

		/// <summary>
		///		Initialize new instance wraps <see cref="String"/>.
		/// </summary>
		public MessagePackObject( string value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = new MessagePackString( value );
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="IList&lt;MessagePackObject&gt;"/>.
		/// </summary>
		public MessagePackObject( IList<MessagePackObject> value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = value;
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="MessagePackObjectDictionary"/>.
		/// </summary>
		public MessagePackObject( MessagePackObjectDictionary value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = value;
		}

		/// <summary>
		///		Initialize new instance wraps raw byte array.
		/// </summary>
		public MessagePackObject( byte[] value )
			: this( value, false ) { }

		/// <summary>
		///		Initialize new instance wraps raw byte array.
		/// </summary>
		internal MessagePackObject( byte[] value, bool mayString )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = new MessagePackString( value );
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="MessagePackString"/>.
		/// </summary>
		/// <param name="messagePackString"><see cref="MessagePackString"/> which represents byte array or UTF-8 encoded string.</param>
		internal MessagePackObject( MessagePackString messagePackString )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = messagePackString;
		}

		#endregion -- Constructors --

#if !SILVERLIGHT

		#region -- ISerializable support --

		/// <summary>
		///		Serialize this instance.
		/// </summary>
		/// <param name="info"><see cref="SerializationInfo"/> to store serialized data.</param>
		/// <param name="context"><see cref="StreamingContext"/> which stores context information.</param>
		/// <exception cref="ArgumentNullException">
		///		<paramref name="info"/> is null.
		/// </exception>
		public void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			if ( info == null )
			{
				throw new ArgumentNullException( "info" );
			}

			Contract.EndContractBlock();

			if ( this.IsNil )
			{
				info.AddValue( "TypeCode", TypeCode.Empty );
				return;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode != null )
			{
				info.AddValue( "TypeCode", typeCode.TypeCode );
				info.AddValue( "Value", this._value );
			}
			else
			{
				info.AddValue( "TypeCode", TypeCode.Object );
				info.AddValue( "Value", this._handleOrTypeCode, this._handleOrTypeCode.GetType() );
			}
		}

		#endregion -- ISerializable support --

#endif

		#region -- Structure Methods --

		/// <summary>
		///		Compare two instances are equal.
		/// </summary>
		/// <param name="obj"><see cref="MessagePackObject"/> instance.</param>
		/// <returns>
		///		If <paramref name="obj"/> is <see cref="MessagePackObject"/> and its value is equal to this instance, then true.
		///		Otherwise false.
		/// </returns>
		public override bool Equals( Object obj )
		{
			if ( obj == null || !( obj is MessagePackObject ) )
			{
				return false;
			}
			else
			{
				return this.Equals( ( MessagePackObject )obj );
			}
		}

		/// <summary>
		///		Compare two instances are equal.
		/// </summary>
		/// <param name="other"><see cref="MessagePackObject"/> instance.</param>
		/// <returns>
		///		Whether value of <paramref name="other"/> is equal to this instance or not.
		/// </returns>
		public bool Equals( MessagePackObject other )
		{
			if ( this._handleOrTypeCode == null )
			{
				return other._handleOrTypeCode == null;
			}
			else if ( other._handleOrTypeCode == null )
			{
				return false;
			}

			var valueTypeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( valueTypeCode != null )
			{
				var otherValuetypeCode = other._handleOrTypeCode as ValueTypeCode;
				if ( otherValuetypeCode == null )
				{
					return false;
				}

				switch ( valueTypeCode.TypeCode )
				{
					case TypeCode.Boolean:
					{
						if ( otherValuetypeCode.TypeCode != TypeCode.Boolean )
						{
							return false;
						}

						return ( bool )this == ( bool )other;
					}
					case TypeCode.SByte:
					case TypeCode.Int16:
					case TypeCode.Int32:
					case TypeCode.Int64:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return this._value == other._value;
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								var thisAsInt64 = unchecked( ( long )this._value );
								if ( thisAsInt64 < 0L )
								{
									return false;
								}

								return this._value == other._value;
							}
							case TypeCode.Single:
							{
								return unchecked( ( long )other._value ) == ( float )other;
							}
							case TypeCode.Double:
							{
								return unchecked( ( long )other._value ) == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Byte:
					case TypeCode.UInt16:
					case TypeCode.UInt32:
					case TypeCode.UInt64:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								var otherAsInt64 = unchecked( ( long )other._value );
								if ( otherAsInt64 < 0L )
								{
									return false;
								}

								return this._value == other._value;
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return this._value == other._value;
							}
							case TypeCode.Single:
							{
								return this._value == ( float )other;
							}
							case TypeCode.Double:
							{
								return this._value == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Single:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return ( float )this == unchecked( ( long )other._value );
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return ( float )this == other._value;
							}
							case TypeCode.Single:
							{
								// Cannot compare _value because there might be not normalized.
								return ( float )this == ( float )other;
							}
							case TypeCode.Double:
							{
								return ( float )this == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Double:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return ( double )this == unchecked( ( long )other._value );
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return ( double )this == other._value;
							}
							case TypeCode.Single:
							{
								return ( double )this == ( float )other;
							}
							case TypeCode.Double:
							{
								// Cannot compare _value because there might be not normalized.
								return ( double )this == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					default:
					{
						// Unsigned
						return this._value == other._value;
					}
				}
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					var otherAsArray = other._handleOrTypeCode as IList<MessagePackObject>;
					if ( otherAsArray == null )
					{
						return false;
					}

					return asArray.SequenceEqual( otherAsArray );
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					var otherAsMap = other._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
					if ( otherAsMap == null )
					{
						return false;
					}

					if ( asMap.Count != otherAsMap.Count )
					{
						return false;
					}

					foreach ( var kv in asMap )
					{
						MessagePackObject value;
						if ( !otherAsMap.TryGetValue( kv.Key, out value ) )
						{
							return false;
						}

						if ( kv.Value != value )
						{
							return false;
						}
					}

					return true;
				}
			}

			{
				var asMps = this._handleOrTypeCode as MessagePackString;
				if ( asMps != null )
				{
					return asMps.Equals( other._handleOrTypeCode as MessagePackString );
				}
			}

			Debug.Assert( false, String.Format( "Unkown handle type this:'{0}'(value: '{1}'), other:'{2}'(value: '{3}')", this._handleOrTypeCode.GetType(), this._handleOrTypeCode, other._handleOrTypeCode.GetType(), other._handleOrTypeCode ) );
			return this._handleOrTypeCode.Equals( other._handleOrTypeCode );
		}

		/// <summary>
		///		Get hash code of this instance.
		/// </summary>
		/// <returns>Hash code of this instance.</returns>
		public override int GetHashCode()
		{
			if ( this._handleOrTypeCode == null )
			{
				return 0;
			}

			if ( this._handleOrTypeCode is ValueTypeCode )
			{
				return this._value.GetHashCode();
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					// TODO: big array support...
					return asArray.Aggregate( 0, ( hash, item ) => hash ^ item.GetHashCode() );
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					// TODO: big map support...
					return asMap.Aggregate( 0, ( hash, item ) => hash ^ item.GetHashCode() );
				}
			}

			var asMps = this._handleOrTypeCode as MessagePackString;
			if ( asMps != null )
			{
				return asMps.GetHashCode();
			}
			else
			{
				Contract.Assert( false, String.Format( "(this._handleOrTypeCode is string) but {0}", this._handleOrTypeCode.GetType() ) );
				return 0;
			}
		}

		/// <summary>
		///		Get string representation of this object.
		/// </summary>
		/// <returns>String representation of this object.</returns>
		/// <remarks>
		///		<note>
		///			DO NOT use this value programmically. 
		///			The purpose of this method is informational, so format of this value subject to change.
		///		</note>
		/// </remarks>
		public override string ToString()
		{
			if ( this._handleOrTypeCode == null )
			{
				return String.Empty;
			}

			ValueTypeCode valueTypeCode;
			if ( ( valueTypeCode = this._handleOrTypeCode as ValueTypeCode ) != null )
			{
				switch ( valueTypeCode.TypeCode )
				{
					case TypeCode.Boolean:
					{
						return this.AsBoolean().ToString();
					}
					case TypeCode.Double:
					{
						return this.AsDouble().ToString( CultureInfo.InvariantCulture );
					}
					case TypeCode.Single:
					{
						return this.AsSingle().ToString( CultureInfo.InvariantCulture );
					}
					default:
					{
						return this._value.ToString();
					}
				}
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					// TODO: big array support...
					if ( asArray.Count == 0 )
					{
						return "[]";
					}

					var sb = new StringBuilder( "[" ).Append( asArray[ 0 ] );
					return asArray.Skip( 1 ).Aggregate( sb, ( buffer, item ) => buffer.Append( ", " ).Append( item.ToString() ) ).Append( "]" ).ToString();
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					// TODO: big map support...
					if ( asMap.Count == 0 )
					{
						return "{}";
					}

					var first = asMap.First();
					var sb = new StringBuilder( "{" ).Append( first.Key.ToString() ).Append( " : " ).Append( first.Value.ToString() );
					return asMap.Skip( 1 ).Aggregate( sb, ( buffer, item ) => buffer.Append( ", " ).Append( item.Key.ToString() ).Append( " : " ).Append( item.Value.ToString() ) ).Append( "}" ).ToString();
				}
			}

			{
				var asBinary = this._handleOrTypeCode as MessagePackString;
				if ( asBinary != null )
				{
					// TODO: big array support...
					var asString = asBinary.TryGetString();
					if ( asString != null )
					{
						return asString;
					}

					var asBlob = asBinary.UnsafeGetBuffer();
					if ( asBlob != null )
					{
						return BitConverter.ToString( asBlob );
					}

					return String.Empty;
				}
			}

			// may be string
			Contract.Assert( false, String.Format( "(this._handleOrTypeCode is string) but {0}", this._handleOrTypeCode.GetType() ) );
			return this._handleOrTypeCode.ToString();
		}

		#endregion -- Structure Methods --

		#region -- Type Of Methods --

		/// <summary>
		///		Determine whether the underlying value of this instance is specified type or not.
		/// </summary>
		/// <typeparam name="T">Target type.</typeparam>
		/// <returns>If the underlying value of this instance is <typeparamref name="T"/> then true, otherwise false.</returns>
		public bool? IsTypeOf<T>()
		{
			return this.IsTypeOf( typeof( T ) );
		}

		/// <summary>
		///		Determine whether the underlying value of this instance is specified type or not.
		/// </summary>
		/// <param name="type">Target type.</param>
		/// <returns>If the underlying value of this instance is <paramref name="type"/> then true, otherwise false.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="type"/> is null.</exception>
		public bool? IsTypeOf( Type type )
		{
			if ( type == null )
			{
				throw new ArgumentNullException( "type" );
			}

			Contract.EndContractBlock();

			if ( this._handleOrTypeCode == null )
			{
				return null;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				if ( type == typeof( string ) )
				{
					return this._handleOrTypeCode is MessagePackString;
				}

				if ( type == typeof( byte[] ) )
				{
					return this._handleOrTypeCode is MessagePackString;
				}

				// Can IEnumerable<byte>
				if ( typeof( IEnumerable<MessagePackObject> ).IsAssignableFrom( type )
					&& this._handleOrTypeCode is MessagePackString )
				{
					return true;
				}

				return type.IsAssignableFrom( this._handleOrTypeCode.GetType() );
			}

			// Lifting support.
			switch ( Type.GetTypeCode( type ) )
			{
				case TypeCode.SByte:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 );
				}
				case TypeCode.Byte:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte );
				}
				case TypeCode.Int16:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 );
				}
				case TypeCode.UInt16:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort );
				}
				case TypeCode.Int32:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( int )
						|| ( typeCode.Type == typeof( uint ) && this._value < 0x80000000 );
				}
				case TypeCode.UInt32:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort )
						|| ( typeCode.Type == typeof( int ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( uint );
				}
				case TypeCode.Int64:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( int )
						|| ( typeCode.Type == typeof( uint ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( long )
						|| ( typeCode.Type == typeof( ulong ) && this._value < ( 0x80000000 << 32 ) );
				}
				case TypeCode.UInt64:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort )
						|| ( typeCode.Type == typeof( int ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( uint )
						|| ( typeCode.Type == typeof( long ) && this._value < ( 0x80000000 << 32 ) )
						|| typeCode.Type == typeof( ulong );
				}
				case TypeCode.Double:
				{
					return
						typeCode.Type == typeof( float )
						|| typeCode.Type == typeof( double );
				}
			}

			return typeCode.Type == type;
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps raw binary (or string) or not.
		/// </summary>
		/// <value>This instance wraps raw binary (or string) then true.</value>
		public bool IsRaw
		{
			get { return !this.IsNil && ( this._handleOrTypeCode is string || this._handleOrTypeCode is IEnumerable<byte> ); }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps list (array) or not.
		/// </summary>
		/// <value>This instance wraps list (array) then true.</value>
		public bool IsList
		{
			get { return !this.IsNil && this._handleOrTypeCode is IList<MessagePackObject>; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps list (array) or not.
		/// </summary>
		/// <value>This instance wraps list (array) then true.</value>
		public bool IsArray
		{
			get { return this.IsList; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps dictionary (map) or not.
		/// </summary>
		/// <value>This instance wraps dictionary (map) then true.</value>
		public bool IsDictionary
		{
			get { return !this.IsNil && this._handleOrTypeCode is IDictionary<MessagePackObject, MessagePackObject>; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps dictionary (map) or not.
		/// </summary>
		/// <value>This instance wraps dictionary (map) then true.</value>
		public bool IsMap
		{
			get { return this.IsDictionary; }
		}

		/// <summary>
		///		Get underlying type of this instance.
		/// </summary>
		/// <returns>Underlying <see cref="Type"/>.</returns>
		public Type GetUnderlyingType()
		{
			if ( this._handleOrTypeCode == null )
			{
				return null;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				var asMps = this._handleOrTypeCode as MessagePackString;
				if ( asMps != null )
				{
					return asMps.GetUnderlyingType();
				}
				else
				{
					return this._handleOrTypeCode.GetType();
				}
			}
			else
			{
				return typeCode.Type;
			}
		}

		#endregion -- Type Of Methods --

		/// <summary>
		///		Pack this instance itself using specified <see cref="Packer"/>.
		/// </summary>
		/// <param name="packer"><see cref="Packer"/>.</param>
		/// <param name="options">Packing options. This value can be null.</param>
		/// <exception cref="ArgumentNullException"><paramref name="packer"/> is null.</exception>
		public void PackToMessage( Packer packer, PackingOptions options )
		{
			if ( packer == null )
			{
				throw new ArgumentNullException( "packer" );
			}

			Contract.EndContractBlock();

			if ( this._handleOrTypeCode == null )
			{
				packer.PackNull();
				return;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				packer.PackObject( this._handleOrTypeCode, options );
				return;
			}

			switch ( typeCode.TypeCode )
			{
				case TypeCode.Single:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( float )this );
					}
					else
					{
						packer.PackStrict( ( float )this );
					}

					return;
				}
				case TypeCode.Double:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( double )this );
					}
					else
					{
						packer.PackStrict( ( double )this );
					}

					return;
				}
				case TypeCode.SByte:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( sbyte )this );
					}
					else
					{
						packer.PackStrict( ( sbyte )this );
					}

					return;
				}
				case TypeCode.Int16:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( short )this );
					}
					else
					{
						packer.PackStrict( ( short )this );
					}

					return;
				}
				case TypeCode.Int32:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( int )this );
					}
					else
					{
						packer.PackStrict( ( int )this );
					}

					return;
				}
				case TypeCode.Int64:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( long )this );
					}
					else
					{
						packer.PackStrict( ( long )this );
					}

					return;
				}
				case TypeCode.Byte:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( byte )this );
					}
					else
					{
						packer.PackStrict( ( byte )this );
					}

					return;
				}
				case TypeCode.UInt16:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( ushort )this );
					}
					else
					{
						packer.PackStrict( ( ushort )this );
					}

					return;
				}
				case TypeCode.UInt32:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( uint )this );
					}
					else
					{
						packer.PackStrict( ( uint )this );
					}

					return;
				}
				case TypeCode.UInt64:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( this._value );
					}
					else
					{
						packer.PackStrict( this._value );
					}

					return;
				}
				case TypeCode.Boolean:
				{
					packer.Pack( this._value != 0 );
					return;
				}
				default:
				{
					Contract.Assume( false, "Unexpected type code :" + typeCode.TypeCode );
					packer.Pack( this._value, options );
					return;
				}
			}
		}

		#region -- Primitive Type Conversion Methods --

		/// <summary>
		///		Get underlying value as raw byte.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public byte[] AsBinary()
		{
			VerifyUnderlyingType<byte[]>( this, null );

			if ( this.IsNil )
			{
				return null;
			}

			return ( this._handleOrTypeCode as MessagePackString ).GetBytes();
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsString()
		{
			return this.AsStringUtf8();
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsString( Encoding encoding )
		{
			if ( encoding == null )
			{
				throw new ArgumentNullException( "encoding" );
			}

			Contract.EndContractBlock();

			// Short path to return just return not-encoded string.
			var asString = this._handleOrTypeCode as string;
			if ( asString != null )
			{
				return asString;
			}

			if ( this.IsNil )
			{
				return null;
			}

			VerifyUnderlyingType<MessagePackString>( this, null );

			try
			{
				var asBytes = this._handleOrTypeCode as MessagePackString;
				if ( asBytes.UnsafeGetBuffer() == null )
				{
					return null;
				}

				return encoding.GetString( asBytes.UnsafeGetBuffer(), 0, asBytes.UnsafeGetBuffer().Length );
			}
			catch ( ArgumentException ex )
			{
				throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, "Not '{0}' string.", encoding.WebName ), ex );
			}
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsStringUtf8()
		{
			return this.AsString( Encoding.UTF8 );
		}

		/// <summary>
		///		Get underlying value as UTF-16 string.
		/// </summary>
		/// <returns>Underlying string.</returns>
		/// <remarks>
		///		This method detects BOM. If BOM is not exist, them bytes should be Big-Endian UTF-16.
		/// </remarks>
		public string AsStringUtf16()
		{
			VerifyUnderlyingType<byte[]>( this, null );
			Contract.EndContractBlock();<commit_msg>Avoid deserialization cache miss<commit_after>﻿#region -- License Terms --
//
// MessagePack for CLI
//
// Copyright (C) 2010 FUJIWARA, Yusuke
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//
#endregion -- License Terms --

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

namespace MsgPack
{
	partial struct MessagePackObject
#if !SILVERLIGHT
 : ISerializable
#endif
	{
		#region -- Type Code Constants --

		private static readonly ValueTypeCode _sbyteTypeCode = new ValueTypeCode( typeof( sbyte ) );
		private static readonly ValueTypeCode _byteTypeCode = new ValueTypeCode( typeof( byte ) );
		private static readonly ValueTypeCode _int16TypeCode = new ValueTypeCode( typeof( short ) );
		private static readonly ValueTypeCode _uint16TypeCode = new ValueTypeCode( typeof( ushort ) );
		private static readonly ValueTypeCode _int32TypeCode = new ValueTypeCode( typeof( int ) );
		private static readonly ValueTypeCode _uint32TypeCode = new ValueTypeCode( typeof( uint ) );
		private static readonly ValueTypeCode _int64TypeCode = new ValueTypeCode( typeof( long ) );
		private static readonly ValueTypeCode _uint64TypeCode = new ValueTypeCode( typeof( ulong ) );
		private static readonly ValueTypeCode _singleTypeCode = new ValueTypeCode( typeof( float ) );
		private static readonly ValueTypeCode _doubleTypeCode = new ValueTypeCode( typeof( double ) );
		private static readonly ValueTypeCode _booleanTypeCode = new ValueTypeCode( typeof( bool ) );

		#endregion -- Type Code Constants --

		/// <summary>
		///		Instance represents nil. This is equal to default value.
		/// </summary>
		public static readonly MessagePackObject Nil = new MessagePackObject();

		#region -- Type Code Fields & Properties --

		private readonly object _handleOrTypeCode;

		/// <summary>
		///		Get whether this instance represents nil.
		/// </summary>
		/// <value>If this instance represents nil object, then true.</value>
		public bool IsNil
		{
			get { return this._handleOrTypeCode == null; }
		}

		private readonly ulong _value;

		#endregion -- Type Code Fields & Properties --

		#region -- Constructors --

		/// <summary>
		///		Initialize new instance wraps <see cref="String"/>.
		/// </summary>
		public MessagePackObject( string value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = new MessagePackString( value );
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="IList&lt;MessagePackObject&gt;"/>.
		/// </summary>
		public MessagePackObject( IList<MessagePackObject> value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = value;
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="MessagePackObjectDictionary"/>.
		/// </summary>
		public MessagePackObject( MessagePackObjectDictionary value )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = value;
		}

		/// <summary>
		///		Initialize new instance wraps raw byte array.
		/// </summary>
		public MessagePackObject( byte[] value )
			: this( value, false ) { }

		/// <summary>
		///		Initialize new instance wraps raw byte array.
		/// </summary>
		internal MessagePackObject( byte[] value, bool mayString )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = new MessagePackString( value );
		}

		/// <summary>
		///		Initialize new instance wraps <see cref="MessagePackString"/>.
		/// </summary>
		/// <param name="messagePackString"><see cref="MessagePackString"/> which represents byte array or UTF-8 encoded string.</param>
		internal MessagePackObject( MessagePackString messagePackString )
		{
			// trick: Avoid long boilerplate initialization. See "CLR via C#".
			this = new MessagePackObject();
			this._handleOrTypeCode = messagePackString;
		}

		#endregion -- Constructors --

#if !SILVERLIGHT

		#region -- ISerializable support --

		/// <summary>
		///		Serialize this instance.
		/// </summary>
		/// <param name="info"><see cref="SerializationInfo"/> to store serialized data.</param>
		/// <param name="context"><see cref="StreamingContext"/> which stores context information.</param>
		/// <exception cref="ArgumentNullException">
		///		<paramref name="info"/> is null.
		/// </exception>
		public void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			if ( info == null )
			{
				throw new ArgumentNullException( "info" );
			}

			Contract.EndContractBlock();

			if ( this.IsNil )
			{
				info.AddValue( "TypeCode", TypeCode.Empty );
				return;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode != null )
			{
				info.AddValue( "TypeCode", typeCode.TypeCode );
				info.AddValue( "Value", this._value );
			}
			else
			{
				info.AddValue( "TypeCode", TypeCode.Object );
				info.AddValue( "Value", this._handleOrTypeCode, this._handleOrTypeCode.GetType() );
			}
		}

		#endregion -- ISerializable support --

#endif

		#region -- Structure Methods --

		/// <summary>
		///		Compare two instances are equal.
		/// </summary>
		/// <param name="obj"><see cref="MessagePackObject"/> instance.</param>
		/// <returns>
		///		If <paramref name="obj"/> is <see cref="MessagePackObject"/> and its value is equal to this instance, then true.
		///		Otherwise false.
		/// </returns>
		public override bool Equals( Object obj )
		{
			if ( obj == null || !( obj is MessagePackObject ) )
			{
				return false;
			}
			else
			{
				return this.Equals( ( MessagePackObject )obj );
			}
		}

		/// <summary>
		///		Compare two instances are equal.
		/// </summary>
		/// <param name="other"><see cref="MessagePackObject"/> instance.</param>
		/// <returns>
		///		Whether value of <paramref name="other"/> is equal to this instance or not.
		/// </returns>
		public bool Equals( MessagePackObject other )
		{
			if ( this._handleOrTypeCode == null )
			{
				return other._handleOrTypeCode == null;
			}
			else if ( other._handleOrTypeCode == null )
			{
				return false;
			}

			var valueTypeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( valueTypeCode != null )
			{
				var otherValuetypeCode = other._handleOrTypeCode as ValueTypeCode;
				if ( otherValuetypeCode == null )
				{
					return false;
				}

				switch ( valueTypeCode.TypeCode )
				{
					case TypeCode.Boolean:
					{
						if ( otherValuetypeCode.TypeCode != TypeCode.Boolean )
						{
							return false;
						}

						return ( bool )this == ( bool )other;
					}
					case TypeCode.SByte:
					case TypeCode.Int16:
					case TypeCode.Int32:
					case TypeCode.Int64:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return this._value == other._value;
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								var thisAsInt64 = unchecked( ( long )this._value );
								if ( thisAsInt64 < 0L )
								{
									return false;
								}

								return this._value == other._value;
							}
							case TypeCode.Single:
							{
								return unchecked( ( long )other._value ) == ( float )other;
							}
							case TypeCode.Double:
							{
								return unchecked( ( long )other._value ) == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Byte:
					case TypeCode.UInt16:
					case TypeCode.UInt32:
					case TypeCode.UInt64:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								var otherAsInt64 = unchecked( ( long )other._value );
								if ( otherAsInt64 < 0L )
								{
									return false;
								}

								return this._value == other._value;
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return this._value == other._value;
							}
							case TypeCode.Single:
							{
								return this._value == ( float )other;
							}
							case TypeCode.Double:
							{
								return this._value == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Single:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return ( float )this == unchecked( ( long )other._value );
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return ( float )this == other._value;
							}
							case TypeCode.Single:
							{
								// Cannot compare _value because there might be not normalized.
								return ( float )this == ( float )other;
							}
							case TypeCode.Double:
							{
								return ( float )this == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					case TypeCode.Double:
					{
						switch ( otherValuetypeCode.TypeCode )
						{
							case TypeCode.SByte:
							case TypeCode.Int16:
							case TypeCode.Int32:
							case TypeCode.Int64:
							{
								return ( double )this == unchecked( ( long )other._value );
							}
							case TypeCode.Byte:
							case TypeCode.UInt16:
							case TypeCode.UInt32:
							case TypeCode.UInt64:
							{
								return ( double )this == other._value;
							}
							case TypeCode.Single:
							{
								return ( double )this == ( float )other;
							}
							case TypeCode.Double:
							{
								// Cannot compare _value because there might be not normalized.
								return ( double )this == ( double )other;
							}
							default:
							{
								return false;
							}
						}
					}
					default:
					{
						// Unsigned
						return this._value == other._value;
					}
				}
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					var otherAsArray = other._handleOrTypeCode as IList<MessagePackObject>;
					if ( otherAsArray == null )
					{
						return false;
					}

					return asArray.SequenceEqual( otherAsArray );
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					var otherAsMap = other._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
					if ( otherAsMap == null )
					{
						return false;
					}

					if ( asMap.Count != otherAsMap.Count )
					{
						return false;
					}

					foreach ( var kv in asMap )
					{
						MessagePackObject value;
						if ( !otherAsMap.TryGetValue( kv.Key, out value ) )
						{
							return false;
						}

						if ( kv.Value != value )
						{
							return false;
						}
					}

					return true;
				}
			}

			{
				var asMps = this._handleOrTypeCode as MessagePackString;
				if ( asMps != null )
				{
					return asMps.Equals( other._handleOrTypeCode as MessagePackString );
				}
			}

			Debug.Assert( false, String.Format( "Unkown handle type this:'{0}'(value: '{1}'), other:'{2}'(value: '{3}')", this._handleOrTypeCode.GetType(), this._handleOrTypeCode, other._handleOrTypeCode.GetType(), other._handleOrTypeCode ) );
			return this._handleOrTypeCode.Equals( other._handleOrTypeCode );
		}

		/// <summary>
		///		Get hash code of this instance.
		/// </summary>
		/// <returns>Hash code of this instance.</returns>
		public override int GetHashCode()
		{
			if ( this._handleOrTypeCode == null )
			{
				return 0;
			}

			if ( this._handleOrTypeCode is ValueTypeCode )
			{
				return this._value.GetHashCode();
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					// TODO: big array support...
					return asArray.Aggregate( 0, ( hash, item ) => hash ^ item.GetHashCode() );
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					// TODO: big map support...
					return asMap.Aggregate( 0, ( hash, item ) => hash ^ item.GetHashCode() );
				}
			}

			var asMps = this._handleOrTypeCode as MessagePackString;
			if ( asMps != null )
			{
				return asMps.GetHashCode();
			}
			else
			{
				Contract.Assert( false, String.Format( "(this._handleOrTypeCode is string) but {0}", this._handleOrTypeCode.GetType() ) );
				return 0;
			}
		}

		/// <summary>
		///		Get string representation of this object.
		/// </summary>
		/// <returns>String representation of this object.</returns>
		/// <remarks>
		///		<note>
		///			DO NOT use this value programmically. 
		///			The purpose of this method is informational, so format of this value subject to change.
		///		</note>
		/// </remarks>
		public override string ToString()
		{
			if ( this._handleOrTypeCode == null )
			{
				return String.Empty;
			}

			ValueTypeCode valueTypeCode;
			if ( ( valueTypeCode = this._handleOrTypeCode as ValueTypeCode ) != null )
			{
				switch ( valueTypeCode.TypeCode )
				{
					case TypeCode.Boolean:
					{
						return this.AsBoolean().ToString();
					}
					case TypeCode.Double:
					{
						return this.AsDouble().ToString( CultureInfo.InvariantCulture );
					}
					case TypeCode.Single:
					{
						return this.AsSingle().ToString( CultureInfo.InvariantCulture );
					}
					default:
					{
						return this._value.ToString();
					}
				}
			}

			{
				var asArray = this._handleOrTypeCode as IList<MessagePackObject>;
				if ( asArray != null )
				{
					// TODO: big array support...
					if ( asArray.Count == 0 )
					{
						return "[]";
					}

					var sb = new StringBuilder( "[" ).Append( asArray[ 0 ] );
					return asArray.Skip( 1 ).Aggregate( sb, ( buffer, item ) => buffer.Append( ", " ).Append( item.ToString() ) ).Append( "]" ).ToString();
				}
			}

			{
				var asMap = this._handleOrTypeCode as IDictionary<MessagePackObject, MessagePackObject>;
				if ( asMap != null )
				{
					// TODO: big map support...
					if ( asMap.Count == 0 )
					{
						return "{}";
					}

					var first = asMap.First();
					var sb = new StringBuilder( "{" ).Append( first.Key.ToString() ).Append( " : " ).Append( first.Value.ToString() );
					return asMap.Skip( 1 ).Aggregate( sb, ( buffer, item ) => buffer.Append( ", " ).Append( item.Key.ToString() ).Append( " : " ).Append( item.Value.ToString() ) ).Append( "}" ).ToString();
				}
			}

			{
				var asBinary = this._handleOrTypeCode as MessagePackString;
				if ( asBinary != null )
				{
					// TODO: big array support...
					var asString = asBinary.TryGetString();
					if ( asString != null )
					{
						return asString;
					}

					var asBlob = asBinary.UnsafeGetBuffer();
					if ( asBlob != null )
					{
						return BitConverter.ToString( asBlob );
					}

					return String.Empty;
				}
			}

			// may be string
			Contract.Assert( false, String.Format( "(this._handleOrTypeCode is string) but {0}", this._handleOrTypeCode.GetType() ) );
			return this._handleOrTypeCode.ToString();
		}

		#endregion -- Structure Methods --

		#region -- Type Of Methods --

		/// <summary>
		///		Determine whether the underlying value of this instance is specified type or not.
		/// </summary>
		/// <typeparam name="T">Target type.</typeparam>
		/// <returns>If the underlying value of this instance is <typeparamref name="T"/> then true, otherwise false.</returns>
		public bool? IsTypeOf<T>()
		{
			return this.IsTypeOf( typeof( T ) );
		}

		/// <summary>
		///		Determine whether the underlying value of this instance is specified type or not.
		/// </summary>
		/// <param name="type">Target type.</param>
		/// <returns>If the underlying value of this instance is <paramref name="type"/> then true, otherwise false.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="type"/> is null.</exception>
		public bool? IsTypeOf( Type type )
		{
			if ( type == null )
			{
				throw new ArgumentNullException( "type" );
			}

			Contract.EndContractBlock();

			if ( this._handleOrTypeCode == null )
			{
				return null;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				if ( type == typeof( string ) )
				{
					return this._handleOrTypeCode is MessagePackString;
				}

				if ( type == typeof( byte[] ) )
				{
					return this._handleOrTypeCode is MessagePackString;
				}

				// Can IEnumerable<byte>
				if ( typeof( IEnumerable<MessagePackObject> ).IsAssignableFrom( type )
					&& this._handleOrTypeCode is MessagePackString )
				{
					return true;
				}

				return type.IsAssignableFrom( this._handleOrTypeCode.GetType() );
			}

			// Lifting support.
			switch ( Type.GetTypeCode( type ) )
			{
				case TypeCode.SByte:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 );
				}
				case TypeCode.Byte:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte );
				}
				case TypeCode.Int16:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 );
				}
				case TypeCode.UInt16:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort );
				}
				case TypeCode.Int32:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( int )
						|| ( typeCode.Type == typeof( uint ) && this._value < 0x80000000 );
				}
				case TypeCode.UInt32:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort )
						|| ( typeCode.Type == typeof( int ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( uint );
				}
				case TypeCode.Int64:
				{
					return
						typeCode.Type == typeof( sbyte )
						|| ( typeCode.Type == typeof( byte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( short )
						|| ( typeCode.Type == typeof( ushort ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( int )
						|| ( typeCode.Type == typeof( uint ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( long )
						|| ( typeCode.Type == typeof( ulong ) && this._value < ( 0x80000000 << 32 ) );
				}
				case TypeCode.UInt64:
				{
					return
						( typeCode.Type == typeof( sbyte ) && this._value < 0x80 )
						|| typeCode.Type == typeof( byte )
						|| ( typeCode.Type == typeof( short ) && this._value < 0x8000 )
						|| typeCode.Type == typeof( ushort )
						|| ( typeCode.Type == typeof( int ) && this._value < 0x80000000 )
						|| typeCode.Type == typeof( uint )
						|| ( typeCode.Type == typeof( long ) && this._value < ( 0x80000000 << 32 ) )
						|| typeCode.Type == typeof( ulong );
				}
				case TypeCode.Double:
				{
					return
						typeCode.Type == typeof( float )
						|| typeCode.Type == typeof( double );
				}
			}

			return typeCode.Type == type;
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps raw binary (or string) or not.
		/// </summary>
		/// <value>This instance wraps raw binary (or string) then true.</value>
		public bool IsRaw
		{
			get { return !this.IsNil && ( this._handleOrTypeCode is string || this._handleOrTypeCode is IEnumerable<byte> ); }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps list (array) or not.
		/// </summary>
		/// <value>This instance wraps list (array) then true.</value>
		public bool IsList
		{
			get { return !this.IsNil && this._handleOrTypeCode is IList<MessagePackObject>; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps list (array) or not.
		/// </summary>
		/// <value>This instance wraps list (array) then true.</value>
		public bool IsArray
		{
			get { return this.IsList; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps dictionary (map) or not.
		/// </summary>
		/// <value>This instance wraps dictionary (map) then true.</value>
		public bool IsDictionary
		{
			get { return !this.IsNil && this._handleOrTypeCode is IDictionary<MessagePackObject, MessagePackObject>; }
		}

		/// <summary>
		///		Get the value indicates whether this instance wraps dictionary (map) or not.
		/// </summary>
		/// <value>This instance wraps dictionary (map) then true.</value>
		public bool IsMap
		{
			get { return this.IsDictionary; }
		}

		/// <summary>
		///		Get underlying type of this instance.
		/// </summary>
		/// <returns>Underlying <see cref="Type"/>.</returns>
		public Type GetUnderlyingType()
		{
			if ( this._handleOrTypeCode == null )
			{
				return null;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				var asMps = this._handleOrTypeCode as MessagePackString;
				if ( asMps != null )
				{
					return asMps.GetUnderlyingType();
				}
				else
				{
					return this._handleOrTypeCode.GetType();
				}
			}
			else
			{
				return typeCode.Type;
			}
		}

		#endregion -- Type Of Methods --

		/// <summary>
		///		Pack this instance itself using specified <see cref="Packer"/>.
		/// </summary>
		/// <param name="packer"><see cref="Packer"/>.</param>
		/// <param name="options">Packing options. This value can be null.</param>
		/// <exception cref="ArgumentNullException"><paramref name="packer"/> is null.</exception>
		public void PackToMessage( Packer packer, PackingOptions options )
		{
			if ( packer == null )
			{
				throw new ArgumentNullException( "packer" );
			}

			Contract.EndContractBlock();

			if ( this._handleOrTypeCode == null )
			{
				packer.PackNull();
				return;
			}

			var typeCode = this._handleOrTypeCode as ValueTypeCode;
			if ( typeCode == null )
			{
				packer.PackObject( this._handleOrTypeCode, options );
				return;
			}

			switch ( typeCode.TypeCode )
			{
				case TypeCode.Single:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( float )this );
					}
					else
					{
						packer.PackStrict( ( float )this );
					}

					return;
				}
				case TypeCode.Double:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( double )this );
					}
					else
					{
						packer.PackStrict( ( double )this );
					}

					return;
				}
				case TypeCode.SByte:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( sbyte )this );
					}
					else
					{
						packer.PackStrict( ( sbyte )this );
					}

					return;
				}
				case TypeCode.Int16:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( short )this );
					}
					else
					{
						packer.PackStrict( ( short )this );
					}

					return;
				}
				case TypeCode.Int32:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( int )this );
					}
					else
					{
						packer.PackStrict( ( int )this );
					}

					return;
				}
				case TypeCode.Int64:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( long )this );
					}
					else
					{
						packer.PackStrict( ( long )this );
					}

					return;
				}
				case TypeCode.Byte:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( byte )this );
					}
					else
					{
						packer.PackStrict( ( byte )this );
					}

					return;
				}
				case TypeCode.UInt16:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( ushort )this );
					}
					else
					{
						packer.PackStrict( ( ushort )this );
					}

					return;
				}
				case TypeCode.UInt32:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( ( uint )this );
					}
					else
					{
						packer.PackStrict( ( uint )this );
					}

					return;
				}
				case TypeCode.UInt64:
				{
					if ( options == null || !options.IsStrict )
					{
						packer.Pack( this._value );
					}
					else
					{
						packer.PackStrict( this._value );
					}

					return;
				}
				case TypeCode.Boolean:
				{
					packer.Pack( this._value != 0 );
					return;
				}
				default:
				{
					Contract.Assume( false, "Unexpected type code :" + typeCode.TypeCode );
					packer.Pack( this._value, options );
					return;
				}
			}
		}

		#region -- Primitive Type Conversion Methods --

		/// <summary>
		///		Get underlying value as raw byte.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public byte[] AsBinary()
		{
			VerifyUnderlyingType<byte[]>( this, null );

			if ( this.IsNil )
			{
				return null;
			}

			return ( this._handleOrTypeCode as MessagePackString ).GetBytes();
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsString()
		{
			return this.AsStringUtf8();
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsString( Encoding encoding )
		{
			if ( encoding == null )
			{
				throw new ArgumentNullException( "encoding" );
			}

			Contract.EndContractBlock();

			// Short path to return just return not-encoded string.
			var asString = this._handleOrTypeCode as string;
			if ( asString != null )
			{
				return asString;
			}

			if ( this.IsNil )
			{
				return null;
			}

			VerifyUnderlyingType<MessagePackString>( this, null );

			try
			{
				var asBytes = this._handleOrTypeCode as MessagePackString;
				if ( asBytes.UnsafeGetBuffer() == null )
				{
					return null;
				}

				if ( encoding is UTF8Encoding )
				{
					return asBytes.TryGetString();
				}

				return encoding.GetString( asBytes.UnsafeGetBuffer(), 0, asBytes.UnsafeGetBuffer().Length );
			}
			catch ( ArgumentException ex )
			{
				throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, "Not '{0}' string.", encoding.WebName ), ex );
			}
		}

		/// <summary>
		///		Get underlying value as UTF8 string.
		/// </summary>
		/// <returns>Underlying raw binary.</returns>
		public string AsStringUtf8()
		{
			return this.AsString( Encoding.UTF8 );
		}

		/// <summary>
		///		Get underlying value as UTF-16 string.
		/// </summary>
		/// <returns>Underlying string.</returns>
		/// <remarks>
		///		This method detects BOM. If BOM is not exist, them bytes should be Big-Endian UTF-16.
		/// </remarks>
		public string AsStringUtf16()
		{
			VerifyUnderlyingType<byte[]>( this, null );
			Contract.EndContractBlock();
{'fraction_non_alphanumeric': 0.12846678993030863, 'fraction_numerical': 0.009155881097994596, 'mean_word_length': 2.2632708708011835, 'pattern_counts': {'":': 0, '<': 371, '<?xml version=': 0, '>': 327, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 12, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4815025', 'n_tokens_mistral': 23641, 'n_tokens_neox': 17142, 'n_words': 4428}