private static TypeCode GetBestNumberTypeComparison(TypeCode a, TypeCode b)
        {
            if (a == b)
            {
                return(a);
            }
            var ua = a.IsUnsignedNumeric();
            var ub = b.IsUnsignedNumeric();

            if (ua == ub)
            {
                return((TypeCode)Math.Max((int)a, (int)b));
            }

            var unsignedCode = ua ? a : b;
            var signedCode   = ua ? b : a;

            if (signedCode > unsignedCode)
            {
                return(signedCode);
            }

            switch (unsignedCode)
            {
            case TypeCode.Byte:
                return(TypeCode.Int16);

            case TypeCode.UInt16:
                return(TypeCode.Int32);

            case TypeCode.UInt32:
                return(TypeCode.Int64);
            }
            return(TypeCode.Double);
        }
        private void WriteNode(BinaryWriter writer, IGraphNode node)
        {
            if (node == null)
            {
                writer.Write((byte)0);
                return;
            }

            switch (node.DataType)
            {
            case SerializedDataType.Boolean:
            case SerializedDataType.Number:
            {
                var      n        = node as IPrimitiveGraphNode;
                byte     code     = 1 << 5;
                var      value    = n.Value;
                TypeCode typeCode = value.GetType().GetTypeCode();
                code |= (byte)typeCode;
                writer.Write(code);
                switch (typeCode)
                {
                case TypeCode.Boolean:
                    writer.Write((bool)value);
                    break;

                case TypeCode.Char:
                    writer.Write((char)value);
                    break;

                case TypeCode.SByte:
                    writer.Write((sbyte)value);
                    break;

                case TypeCode.Byte:
                    writer.Write((byte)value);
                    break;

                case TypeCode.Int16:
                    writer.Write((short)value);
                    break;

                case TypeCode.UInt16:
                    writer.Write((ushort)value);
                    break;

                case TypeCode.Int32:
                    writer.Write((int)value);
                    break;

                case TypeCode.UInt32:
                    writer.Write((uint)value);
                    break;

                case TypeCode.Int64:
                    writer.Write((long)value);
                    break;

                case TypeCode.UInt64:
                    writer.Write((ulong)value);
                    break;

                case TypeCode.Single:
                    writer.Write((float)value);
                    break;

                case TypeCode.Double:
                    writer.Write((double)value);
                    break;

                case TypeCode.Decimal:
                    writer.Write((decimal)value);
                    break;

                case TypeCode.DateTime:
                {
                    var d = (DateTime)value;
                    writer.Write(d.ToFileTimeUtc());
                }
                break;

                default:
                    throw new ArgumentOutOfRangeException("Not supported " + typeCode);
                }
            }
            break;

            case SerializedDataType.String:
            {
                var  n    = node as IStringGraphNode;
                byte code = 2 << 5;
                writer.Write(code);
                writer.Write(n.Value);
            }
            break;

            case SerializedDataType.DataSequence:
            {
                var  nodes = node as ISequenceGraphNode;
                byte code  = 3 << 5;
                writer.Write(code);
                writer.Write(nodes.Length);
                foreach (var n in nodes)
                {
                    WriteNode(writer, n);
                }
            }
            break;

            case SerializedDataType.Type:
            {
                var  n    = node as ITypeGraphNode;
                byte code = 4 << 5;
                writer.Write(code);
                writer.Write(n.TypeId);
            }
            break;

            case SerializedDataType.Object:
            {
                var  n    = node as IObjectGraphNode;
                byte code = 5 << 5;
                if (n.IsReferedMultipleTimes)
                {
                    code |= 1 << 4;
                    writer.Write(code);
                    writer.Write(n.RefId);
                }
                else
                {
                    writer.Write(code);
                    WriteObjectNode(writer, n);
                }
            }
            break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        private IGraphNode ReadNode(BinaryReader reader, Graph graph)
        {
            byte code     = reader.ReadByte();
            byte nodeCode = (byte)(code >> 5);
            byte codeData = (byte)(code & 0x1f);

            switch (nodeCode)
            {
            case 0:
                return(null);

            case 1:                     //SerializedDataType.Boolean or SerializedDataType.Number
            {
                TypeCode  typeCode = (TypeCode)codeData;
                ValueType value;
                switch (typeCode)
                {
                case TypeCode.Boolean:
                    value = reader.ReadBoolean();
                    break;

                case TypeCode.Char:
                    value = reader.ReadChar();
                    break;

                case TypeCode.SByte:
                    value = reader.ReadSByte();
                    break;

                case TypeCode.Byte:
                    value = reader.ReadByte();
                    break;

                case TypeCode.Int16:
                    value = reader.ReadInt16();
                    break;

                case TypeCode.UInt16:
                    value = reader.ReadUInt16();
                    break;

                case TypeCode.Int32:
                    value = reader.ReadInt32();
                    break;

                case TypeCode.UInt32:
                    value = reader.ReadUInt32();
                    break;

                case TypeCode.Int64:
                    value = reader.ReadInt64();
                    break;

                case TypeCode.UInt64:
                    value = reader.ReadUInt64();
                    break;

                case TypeCode.Single:
                    value = reader.ReadSingle();
                    break;

                case TypeCode.Double:
                    value = reader.ReadDouble();
                    break;

                case TypeCode.Decimal:
                    value = reader.ReadDecimal();
                    break;

                case TypeCode.DateTime:
                {
                    long time = reader.ReadInt64();
                    value = DateTime.FromFileTimeUtc(time);
                }
                break;

                default:
                    throw new ArgumentOutOfRangeException("Not supported " + typeCode);
                }
                return(graph.BuildPrimitiveNode(value));
            }

            case 2:                     //SerializedDataType.String
                return(graph.BuildStringNode(reader.ReadString()));

            case 3:                     //SerializedDataType.DataSequence
            {
                var n             = graph.BuildSequenceNode();
                int numOfElements = reader.ReadInt32();
                for (int i = 0; i < numOfElements; i++)
                {
                    var node = ReadNode(reader, graph);
                    n.Add(node);
                }
                return(n);
            }

            case 4:                     //SerializedDataType.Type
            {
                return(graph.GetTypeEntry(reader.ReadByte()));
            }

            case 5:                     //SerializedDataType.Object
            {
                codeData = (byte)(codeData >> 4);
                if (codeData == 1)
                {
                    int refId = reader.ReadInt32();
                    return(graph.GetObjectDataForRefId(refId));
                }
                if (codeData == 0)
                {
                    return(ReadObjectNode(reader, graph));
                }

                throw new Exception("Invalid bytecode");
            }

            default:
                throw new ArgumentOutOfRangeException();
            }
        }