2005-08-02 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / ObjectReader.cs
index c703c04e1814564a9ba247941e5dd37eabd063ae..54f581618a2f757dbd880b12421b1e6eef34a0d1 100644 (file)
@@ -5,6 +5,29 @@
 //   Patrik Torstensson\r
 //\r
 // (C) 2003 Lluis Sanchez Gual\r
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
 \r
 using System;\r
 using System.Runtime.Serialization;\r
@@ -33,7 +56,9 @@ namespace System.Runtime.Serialization.Formatters.Binary
 \r
                object _lastObject = null;\r
                long _lastObjectID = 0;\r
-               long _rootObjectID = 0;\r
+               long _rootObjectID = 0;
+               byte[] arrayBuffer;
+               int ArrayBufferLength = 4096;\r
 \r
                class TypeMetadata\r
                {\r
@@ -124,12 +149,20 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        ReadRefTypeObjectInstance (reader, out objectId, out value, out info);\r
                                        break;\r
 \r
+                               case BinaryElement.UntypedRuntimeObject:\r
+                                       ReadObjectInstance (reader, true, false, out objectId, out value, out info);\r
+                                       break;\r
+\r
+                               case BinaryElement.UntypedExternalObject:\r
+                                       ReadObjectInstance (reader, false, false, out objectId, out value, out info);\r
+                                       break;\r
+\r
                                case BinaryElement.RuntimeObject:\r
-                                       ReadObjectInstance (reader, true, out objectId, out value, out info);\r
+                                       ReadObjectInstance (reader, true, true, out objectId, out value, out info);\r
                                        break;\r
 \r
                                case BinaryElement.ExternalObject:\r
-                                       ReadObjectInstance (reader, false, out objectId, out value, out info);\r
+                                       ReadObjectInstance (reader, false, true, out objectId, out value, out info);\r
                                        break;\r
 \r
                                case BinaryElement.String:\r
@@ -199,11 +232,11 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        _registeredAssemblies [id] = assemblyName;\r
                }\r
 \r
-               private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, out long objectId, out object value, out SerializationInfo info)\r
+               private void ReadObjectInstance (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo, out long objectId, out object value, out SerializationInfo info)\r
                {\r
                        objectId = (long) reader.ReadUInt32 ();\r
 \r
-                       TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject);\r
+                       TypeMetadata metadata = ReadTypeMetadata (reader, isRuntimeObject, hasTypeInfo);\r
                        ReadObjectContent (reader, metadata, objectId, out value, out info);\r
                }\r
 \r
@@ -262,7 +295,8 @@ namespace System.Runtime.Serialization.Formatters.Binary
                private void ReadGenericArray (BinaryReader reader, out long objectId, out object val)\r
                {\r
                        objectId = (long) reader.ReadUInt32 ();\r
-                       ArrayStructure structure = (ArrayStructure) reader.ReadByte();\r
+                       // Array structure
+                       reader.ReadByte();\r
 \r
                        int rank = reader.ReadInt32();\r
 \r
@@ -326,13 +360,190 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        int length = reader.ReadInt32 ();\r
                        Type elementType = ReadType (reader, TypeTag.PrimitiveType);\r
 \r
-                       Array array = Array.CreateInstance (elementType, length);\r
-                       for (int n = 0; n < length; n++)\r
-                               array.SetValue (ReadPrimitiveTypeValue (reader, elementType), n);\r
+                       switch (Type.GetTypeCode (elementType))\r
+                       {\r
+                               case TypeCode.Boolean: {\r
+                                       bool[] arr = new bool [length];\r
+                                       for (int n = 0; n < length; n++) arr [n] = reader.ReadBoolean();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
 \r
-                       val = array;\r
+                               case TypeCode.Byte: {\r
+                                       byte[] arr = new byte [length];\r
+                                       int pos = 0;\r
+                                       while (pos < length) {\r
+                                               int nr = reader.Read (arr, pos, length - pos);\r
+                                               if (nr == 0) break;\r
+                                               pos += nr;\r
+                                       }\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Char: {\r
+                                       char[] arr = new char [length];\r
+                                       int pos = 0;\r
+                                       while (pos < length) {\r
+                                               int nr = reader.Read (arr, pos, length - pos);\r
+                                               if (nr == 0) break;\r
+                                               pos += nr;\r
+                                       }\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.DateTime: {\r
+                                       DateTime[] arr = new DateTime [length];\r
+                                       for (int n = 0; n < length; n++) arr [n] = new DateTime (reader.ReadInt64());\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Decimal: {\r
+                                       Decimal[] arr = new Decimal [length];\r
+                                       for (int n = 0; n < length; n++) arr [n] = reader.ReadDecimal();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Double: {\r
+                                       Double[] arr = new Double [length];
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 8);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadDouble();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Int16: {\r
+                                       short[] arr = new short [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 2);
+                                       else
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadInt16();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Int32: {\r
+                                       int[] arr = new int [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 4);
+                                       else
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadInt32();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Int64: {\r
+                                       long[] arr = new long [length];
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 8);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadInt64();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.SByte: {\r
+                                       sbyte[] arr = new sbyte [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 1);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadSByte();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.Single: {\r
+                                       float[] arr = new float [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 4);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadSingle();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.UInt16: {\r
+                                       ushort[] arr = new ushort [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 2);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt16();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.UInt32: {\r
+                                       uint[] arr = new uint [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 4);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt32();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.UInt64: {\r
+                                       ulong[] arr = new ulong [length];\r
+                                       if (length > 2)\r
+                                               BlockRead (reader, arr, 8);
+                                       else\r
+                                               for (int n = 0; n < length; n++) arr [n] = reader.ReadUInt64();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               case TypeCode.String: {\r
+                                       string[] arr = new string [length];\r
+                                       for (int n = 0; n < length; n++) arr [n] = reader.ReadString();\r
+                                       val = arr;\r
+                                       break;\r
+                               }\r
+\r
+                               default: {\r
+                                       if (elementType == typeof(TimeSpan)) {\r
+                                               TimeSpan[] arr = new TimeSpan [length];\r
+                                               for (int n = 0; n < length; n++) arr [n] = new TimeSpan (reader.ReadInt64 ());\r
+                                               val = arr;\r
+                                       }\r
+                                       else\r
+                                               throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);\r
+                                       break;\r
+                               }\r
+                       }                       \r
                }\r
 \r
+               private void BlockRead (BinaryReader reader, Array array, int dataSize)\r
+               {
+                       int totalSize = Buffer.ByteLength (array);
+                       
+                       if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))
+                               arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];
+                       
+                       int pos = 0;
+                       while (totalSize > 0) {
+                               int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;
+                               int ap = 0;
+                               do {
+                                       int nr = reader.Read (arrayBuffer, ap, size - ap);\r
+                                       if (nr == 0) break;\r
+                                       ap += nr;
+                               } while (ap < size);
+                               
+                               if (!BitConverter.IsLittleEndian && dataSize > 1)
+                                       BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);
+
+                               Buffer.BlockCopy (arrayBuffer, 0, array, pos, size);
+                               totalSize -= size;
+                               pos += size;
+                       }
+               }
+               
+\r
                private void ReadArrayOfObject (BinaryReader reader, out long objectId, out object array)\r
                {\r
                        ReadSimpleArray (reader, typeof (object), out objectId, out array);\r
@@ -359,7 +570,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        val = array;\r
                }\r
 \r
-               private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject)\r
+               private TypeMetadata ReadTypeMetadata (BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo)\r
                {\r
                        TypeMetadata metadata = new TypeMetadata();\r
 \r
@@ -369,17 +580,20 @@ namespace System.Runtime.Serialization.Formatters.Binary
                        Type[] types = new Type[fieldCount];\r
                        string[] names = new string[fieldCount];\r
 \r
-                       TypeTag[] codes = new TypeTag[fieldCount];\r
-\r
                        for (int n=0; n<fieldCount; n++)\r
                                names [n] = reader.ReadString ();\r
 \r
-                       for (int n=0; n<fieldCount; n++)\r
-                               codes [n] = (TypeTag) reader.ReadByte ();\r
-\r
-                       for (int n=0; n<fieldCount; n++)\r
-                               types [n] = ReadType (reader, codes[n]);\r
+                       if (hasTypeInfo)\r
+                       {\r
+                               TypeTag[] codes = new TypeTag[fieldCount];\r
 \r
+                               for (int n=0; n<fieldCount; n++)\r
+                                       codes [n] = (TypeTag) reader.ReadByte ();\r
+       \r
+                               for (int n=0; n<fieldCount; n++)\r
+                                       types [n] = ReadType (reader, codes[n]);\r
+                       }\r
+                       \r
                        // Gets the type\r
 \r
                        if (!isRuntimeObject) \r
@@ -419,7 +633,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                        metadata.MemberInfos = new MemberInfo [fieldCount];\r
                                        for (int n=0; n<fieldCount; n++)\r
                                        {\r
-                                               MemberInfo[] members = null;\r
+                                               FieldInfo field = null;\r
                                                string memberName = names[n];\r
                                                \r
                                                int i = memberName.IndexOf ('+');\r
@@ -429,7 +643,7 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                                        Type t = metadata.Type.BaseType;\r
                                                        while (t != null) {\r
                                                                if (t.Name == baseTypeName) {\r
-                                                                       members = t.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
+                                                                       field = t.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
                                                                        break;\r
                                                                }\r
                                                                else\r
@@ -437,11 +651,14 @@ namespace System.Runtime.Serialization.Formatters.Binary
                                                        }\r
                                                }\r
                                                else\r
-                                                       members = metadata.Type.GetMember (memberName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
+                                                       field = metadata.Type.GetField (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
                                                        \r
-                                               if (members == null || members.Length == 0) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
-                                               if (members.Length > 1) throw new SerializationException ("There are two public members named \"" + names[n] + "\" in the class hirearchy of " + metadata.Type.FullName);\r
-                                               metadata.MemberInfos [n] = members[0];\r
+                                               if (field == null) throw new SerializationException ("Field \"" + names[n] + "\" not found in class " + metadata.Type.FullName);\r
+                                               metadata.MemberInfos [n] = field;\r
+                                               \r
+                                               if (!hasTypeInfo) {\r
+                                                       types [n] = field.FieldType;\r
+                                               }\r
                                        }\r
                                        metadata.MemberNames = null;    // Info now in MemberInfos\r
                                }\r
@@ -570,13 +787,13 @@ namespace System.Runtime.Serialization.Formatters.Binary
                {\r
                        string assemblyName = (string)_registeredAssemblies[assemblyId];\r
 \r
-                       if (_binder == null)\r
-                       {\r
-                               Assembly assembly = Assembly.Load (assemblyName);\r
-                               return assembly.GetType (className, true);\r
+                       if (_binder != null) {\r
+                               Type t = _binder.BindToType (assemblyName, className);\r
+                               if (t != null) return t;\r
                        }\r
-                       else\r
-                               return _binder.BindToType (assemblyName, className);\r
+                               \r
+                       Assembly assembly = Assembly.Load (assemblyName);\r
+                       return assembly.GetType (className, true);\r
                }\r
 \r
                public Type ReadType (BinaryReader reader, TypeTag code)\r