ObjectWriter.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. // ObjectWriter.cs
  2. //
  3. // Author:
  4. // Lluis Sanchez Gual ([email protected])
  5. //
  6. // (C) 2003 Lluis Sanchez Gual
  7. //
  8. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.IO;
  31. using System.Collections;
  32. using System.Runtime.Serialization;
  33. using System.Runtime.Remoting.Messaging;
  34. using System.Reflection;
  35. using System.Globalization;
  36. namespace System.Runtime.Serialization.Formatters.Binary
  37. {
  38. abstract class TypeMetadata
  39. {
  40. public string TypeAssemblyName;
  41. public string InstanceTypeName;
  42. public abstract void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
  43. public abstract void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
  44. public abstract void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data);
  45. public virtual bool IsCompatible (TypeMetadata other)
  46. {
  47. return true;
  48. }
  49. public abstract bool RequiresTypes { get; }
  50. }
  51. abstract class ClrTypeMetadata: TypeMetadata
  52. {
  53. public Type InstanceType;
  54. public ClrTypeMetadata (Type instanceType)
  55. {
  56. InstanceType = instanceType;
  57. InstanceTypeName = instanceType.FullName;
  58. TypeAssemblyName = instanceType.Assembly.FullName;
  59. }
  60. public override bool RequiresTypes {
  61. get { return false; }
  62. }
  63. }
  64. class SerializableTypeMetadata: TypeMetadata
  65. {
  66. Type[] types;
  67. string[] names;
  68. public SerializableTypeMetadata (Type itype, SerializationInfo info)
  69. {
  70. types = new Type [info.MemberCount];
  71. names = new string [info.MemberCount];
  72. SerializationInfoEnumerator e = info.GetEnumerator ();
  73. int n = 0;
  74. while (e.MoveNext ())
  75. {
  76. types[n] = e.ObjectType;
  77. names[n] = e.Name;
  78. n++;
  79. }
  80. TypeAssemblyName = info.AssemblyName;
  81. InstanceTypeName = info.FullTypeName;
  82. }
  83. public override bool IsCompatible (TypeMetadata other)
  84. {
  85. if (!(other is SerializableTypeMetadata)) return false;
  86. SerializableTypeMetadata tm = (SerializableTypeMetadata)other;
  87. if (types.Length != tm.types.Length) return false;
  88. if (TypeAssemblyName != tm.TypeAssemblyName) return false;
  89. if (InstanceTypeName != tm.InstanceTypeName) return false;
  90. for (int n=0; n<types.Length; n++)
  91. {
  92. if (types[n] != tm.types[n]) return false;
  93. if (names[n] != tm.names[n]) return false;
  94. }
  95. return true;
  96. }
  97. public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
  98. {
  99. foreach (Type mtype in types)
  100. {
  101. Type type = mtype;
  102. while (type.IsArray)
  103. type = type.GetElementType();
  104. ow.WriteAssembly (writer, type.Assembly);
  105. }
  106. }
  107. public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
  108. {
  109. writer.Write (types.Length);
  110. // Names of fields
  111. foreach (string name in names)
  112. writer.Write (name);
  113. // Types of fields
  114. foreach (Type type in types)
  115. ObjectWriter.WriteTypeCode (writer, type);
  116. // Type specs of fields
  117. foreach (Type type in types)
  118. ow.WriteTypeSpec (writer, type);
  119. }
  120. public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
  121. {
  122. SerializationInfo info = (SerializationInfo) data;
  123. SerializationInfoEnumerator e = info.GetEnumerator ();
  124. while (e.MoveNext ())
  125. ow.WriteValue (writer, e.ObjectType, e.Value);
  126. }
  127. public override bool RequiresTypes {
  128. get { return true; }
  129. }
  130. }
  131. class MemberTypeMetadata: ClrTypeMetadata
  132. {
  133. MemberInfo[] members;
  134. public MemberTypeMetadata (Type type, StreamingContext context): base (type)
  135. {
  136. members = FormatterServices.GetSerializableMembers (type, context);
  137. }
  138. public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
  139. {
  140. foreach (FieldInfo field in members)
  141. {
  142. Type type = field.FieldType;
  143. while (type.IsArray)
  144. type = type.GetElementType();
  145. ow.WriteAssembly (writer, type.Assembly);
  146. }
  147. }
  148. public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
  149. {
  150. writer.Write (members.Length);
  151. // Names of fields
  152. foreach (FieldInfo field in members)
  153. writer.Write (field.Name);
  154. if (writeTypes) {
  155. // Types of fields
  156. foreach (FieldInfo field in members)
  157. ObjectWriter.WriteTypeCode (writer, field.FieldType);
  158. // Type specs of fields
  159. foreach (FieldInfo field in members)
  160. ow.WriteTypeSpec (writer, field.FieldType);
  161. }
  162. }
  163. public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
  164. {
  165. object[] values = FormatterServices.GetObjectData (data, members);
  166. for (int n=0; n<values.Length; n++)
  167. ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
  168. }
  169. }
  170. internal class ObjectWriter
  171. {
  172. ObjectIDGenerator _idGenerator = new ObjectIDGenerator();
  173. Hashtable _cachedMetadata = new Hashtable();
  174. Queue _pendingObjects = new Queue();
  175. Hashtable _assemblyCache = new Hashtable();
  176. // Type metadata that can be shared with all serializers
  177. static Hashtable _cachedTypes = new Hashtable();
  178. internal static Assembly CorlibAssembly = typeof(string).Assembly;
  179. internal static string CorlibAssemblyName = typeof(string).Assembly.FullName;
  180. ISurrogateSelector _surrogateSelector;
  181. StreamingContext _context;
  182. FormatterAssemblyStyle _assemblyFormat;
  183. FormatterTypeStyle _typeFormat;
  184. byte[] arrayBuffer;
  185. int ArrayBufferLength = 4096;
  186. class MetadataReference
  187. {
  188. public TypeMetadata Metadata;
  189. public long ObjectID;
  190. public MetadataReference (TypeMetadata metadata, long id)
  191. {
  192. Metadata = metadata;
  193. ObjectID = id;
  194. }
  195. }
  196. public ObjectWriter (ISurrogateSelector surrogateSelector, StreamingContext context, FormatterAssemblyStyle assemblyFormat, FormatterTypeStyle typeFormat)
  197. {
  198. _surrogateSelector = surrogateSelector;
  199. _context = context;
  200. _assemblyFormat = assemblyFormat;
  201. _typeFormat = typeFormat;
  202. }
  203. public void WriteObjectGraph (BinaryWriter writer, object obj, Header[] headers)
  204. {
  205. _pendingObjects.Clear();
  206. if (headers != null) QueueObject (headers);
  207. QueueObject (obj);
  208. WriteQueuedObjects (writer);
  209. WriteSerializationEnd (writer);
  210. }
  211. public void QueueObject (object obj)
  212. {
  213. _pendingObjects.Enqueue (obj);
  214. }
  215. public void WriteQueuedObjects (BinaryWriter writer)
  216. {
  217. while (_pendingObjects.Count > 0)
  218. WriteObjectInstance (writer, _pendingObjects.Dequeue(), false);
  219. }
  220. public void WriteObjectInstance (BinaryWriter writer, object obj, bool isValueObject)
  221. {
  222. bool firstTime;
  223. long id;
  224. // If the object is a value type (not boxed) then there is no need
  225. // to register it in the id generator, because it won't have other
  226. // references to it
  227. if (isValueObject) id = _idGenerator.NextId;
  228. else id = _idGenerator.GetId (obj, out firstTime);
  229. if (obj is string) {
  230. WriteString (writer, id, (string)obj);
  231. }
  232. else if (obj is Array) {
  233. WriteArray (writer, id, (Array)obj);
  234. }
  235. else
  236. WriteObject (writer, id, obj);
  237. }
  238. public static void WriteSerializationEnd (BinaryWriter writer)
  239. {
  240. writer.Write ((byte) BinaryElement.End);
  241. }
  242. private void WriteObject (BinaryWriter writer, long id, object obj)
  243. {
  244. object data;
  245. TypeMetadata metadata;
  246. GetObjectData (obj, out metadata, out data);
  247. MetadataReference metadataReference = (MetadataReference)_cachedMetadata [metadata.InstanceTypeName];
  248. if (metadataReference != null && metadata.IsCompatible (metadataReference.Metadata))
  249. {
  250. // An object of the same type has already been serialized
  251. // It is not necessary to write again type metadata
  252. writer.Write ((byte) BinaryElement.RefTypeObject);
  253. writer.Write ((int)id);
  254. writer.Write ((int)metadataReference.ObjectID);
  255. metadata.WriteObjectData (this, writer, data);
  256. return;
  257. }
  258. if (metadataReference == null)
  259. {
  260. metadataReference = new MetadataReference (metadata, id);
  261. _cachedMetadata [metadata.InstanceTypeName] = metadataReference;
  262. }
  263. bool writeTypes = metadata.RequiresTypes || _typeFormat == FormatterTypeStyle.TypesAlways;
  264. BinaryElement objectTag;
  265. int assemblyId;
  266. if (metadata.TypeAssemblyName == CorlibAssemblyName)
  267. {
  268. // A corlib type
  269. objectTag = writeTypes ? BinaryElement.RuntimeObject : BinaryElement.UntypedRuntimeObject;
  270. assemblyId = -1;
  271. }
  272. else
  273. {
  274. objectTag = writeTypes ? BinaryElement.ExternalObject : BinaryElement.UntypedExternalObject;
  275. assemblyId = WriteAssemblyName (writer, metadata.TypeAssemblyName);
  276. }
  277. // Registers the assemblies needed for each field
  278. // If there are assemblies that where not registered before this object,
  279. // write them now
  280. metadata.WriteAssemblies (this, writer);
  281. // Writes the object
  282. writer.Write ((byte) objectTag);
  283. writer.Write ((int)id);
  284. writer.Write (metadata.InstanceTypeName);
  285. metadata.WriteTypeData (this, writer, writeTypes);
  286. if (assemblyId != -1) writer.Write (assemblyId);
  287. metadata.WriteObjectData (this, writer, data);
  288. }
  289. private void GetObjectData (object obj, out TypeMetadata metadata, out object data)
  290. {
  291. Type instanceType = obj.GetType();
  292. // Check if the formatter has a surrogate selector, if it does,
  293. // check if the surrogate selector handles objects of the given type.
  294. if (_surrogateSelector != null)
  295. {
  296. ISurrogateSelector selector;
  297. ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (instanceType, _context, out selector);
  298. if (surrogate != null)
  299. {
  300. SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
  301. surrogate.GetObjectData (obj, info, _context);
  302. metadata = new SerializableTypeMetadata (instanceType, info);
  303. data = info;
  304. return;
  305. }
  306. }
  307. // Check if the object is marked with the Serializable attribute
  308. BinaryCommon.CheckSerializable (instanceType, _surrogateSelector, _context);
  309. ISerializable ser = obj as ISerializable;
  310. if (ser != null)
  311. {
  312. SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
  313. ser.GetObjectData (info, _context);
  314. metadata = new SerializableTypeMetadata (instanceType, info);
  315. data = info;
  316. }
  317. else
  318. {
  319. data = obj;
  320. if (_context.Context != null)
  321. {
  322. // Don't cache metadata info when the Context property is not null sice
  323. // we can't control the number of possible contexts in this case
  324. metadata = new MemberTypeMetadata (instanceType, _context);
  325. return;
  326. }
  327. Hashtable typesTable;
  328. bool isNew = false;
  329. lock (_cachedTypes) {
  330. typesTable = (Hashtable) _cachedTypes [_context.State];
  331. if (typesTable == null) {
  332. typesTable = new Hashtable ();
  333. _cachedTypes [_context.State] = typesTable;
  334. isNew = true;
  335. }
  336. }
  337. metadata = null;
  338. lock (typesTable) {
  339. if (!isNew) {
  340. metadata = (TypeMetadata) typesTable [instanceType];
  341. }
  342. if (metadata == null) {
  343. metadata = CreateMemberTypeMetadata (instanceType);
  344. }
  345. typesTable [instanceType] = metadata;
  346. }
  347. }
  348. }
  349. TypeMetadata CreateMemberTypeMetadata (Type type)
  350. {
  351. if (!BinaryCommon.UseReflectionSerialization) {
  352. Type metaType = CodeGenerator.GenerateMetadataType (type, _context);
  353. return (TypeMetadata) Activator.CreateInstance (metaType);
  354. }
  355. else
  356. return new MemberTypeMetadata (type, _context);
  357. }
  358. private void WriteArray (BinaryWriter writer, long id, Array array)
  359. {
  360. // There are 4 ways of serializing arrays:
  361. // The element GenericArray (7) can be used for all arrays.
  362. // The element ArrayOfPrimitiveType (15) can be used for single-dimensional
  363. // arrays of primitive types
  364. // The element ArrayOfObject (16) can be used for single-dimensional Object arrays
  365. // The element ArrayOfString (17) can be used for single-dimensional string arrays
  366. Type elementType = array.GetType().GetElementType();
  367. if (elementType == typeof (object) && array.Rank == 1) {
  368. WriteObjectArray (writer, id, array);
  369. }
  370. else if (elementType == typeof (string) && array.Rank == 1) {
  371. WriteStringArray (writer, id, array);
  372. }
  373. else if (BinaryCommon.IsPrimitive(elementType) && array.Rank == 1) {
  374. WritePrimitiveTypeArray (writer, id, array);
  375. }
  376. else
  377. WriteGenericArray (writer, id, array);
  378. }
  379. private void WriteGenericArray (BinaryWriter writer, long id, Array array)
  380. {
  381. Type elementType = array.GetType().GetElementType();
  382. // Registers and writes the assembly of the array element type if needed
  383. if (!elementType.IsArray)
  384. WriteAssembly (writer, elementType.Assembly);
  385. // Writes the array
  386. writer.Write ((byte) BinaryElement.GenericArray);
  387. writer.Write ((int)id);
  388. // Write the structure of the array
  389. if (elementType.IsArray)
  390. writer.Write ((byte) ArrayStructure.Jagged);
  391. else if (array.Rank == 1)
  392. writer.Write ((byte) ArrayStructure.SingleDimensional);
  393. else
  394. writer.Write ((byte) ArrayStructure.MultiDimensional);
  395. // Write the number of dimensions and the length
  396. // of each dimension
  397. writer.Write (array.Rank);
  398. for (int n=0; n<array.Rank; n++)
  399. writer.Write (array.GetUpperBound (n) + 1);
  400. // Writes the type
  401. WriteTypeCode (writer, elementType);
  402. WriteTypeSpec (writer, elementType);
  403. // Writes the values. For single-dimension array, a special tag is used
  404. // to represent multiple consecutive null values. I don't know why this
  405. // optimization is not used for multidimensional arrays.
  406. if (array.Rank == 1 && !elementType.IsValueType)
  407. {
  408. WriteSingleDimensionArrayElements (writer, array, elementType);
  409. }
  410. else
  411. {
  412. foreach (object item in array)
  413. WriteValue (writer, elementType, item);
  414. }
  415. }
  416. private void WriteObjectArray (BinaryWriter writer, long id, Array array)
  417. {
  418. writer.Write ((byte) BinaryElement.ArrayOfObject);
  419. writer.Write ((int)id);
  420. writer.Write (array.Length); // Single dimension. Just write the length
  421. WriteSingleDimensionArrayElements (writer, array, typeof (object));
  422. }
  423. private void WriteStringArray (BinaryWriter writer, long id, Array array)
  424. {
  425. writer.Write ((byte) BinaryElement.ArrayOfString);
  426. writer.Write ((int)id);
  427. writer.Write (array.Length); // Single dimension. Just write the length
  428. WriteSingleDimensionArrayElements (writer, array, typeof (string));
  429. }
  430. private void WritePrimitiveTypeArray (BinaryWriter writer, long id, Array array)
  431. {
  432. writer.Write ((byte) BinaryElement.ArrayOfPrimitiveType);
  433. writer.Write ((int)id);
  434. writer.Write (array.Length); // Single dimension. Just write the length
  435. Type elementType = array.GetType().GetElementType();
  436. WriteTypeSpec (writer, elementType);
  437. switch (Type.GetTypeCode (elementType))
  438. {
  439. case TypeCode.Boolean:
  440. foreach (bool item in (bool[]) array)
  441. writer.Write (item);
  442. break;
  443. case TypeCode.Byte:
  444. writer.Write ((byte[]) array);
  445. break;
  446. case TypeCode.Char:
  447. writer.Write ((char[]) array);
  448. break;
  449. case TypeCode.DateTime:
  450. foreach (DateTime item in (DateTime[]) array)
  451. writer.Write (item.Ticks);
  452. break;
  453. case TypeCode.Decimal:
  454. foreach (decimal item in (decimal[]) array)
  455. writer.Write (item);
  456. break;
  457. case TypeCode.Double:
  458. if (array.Length > 2)
  459. BlockWrite (writer, array, 8);
  460. else
  461. foreach (double item in (double[]) array)
  462. writer.Write (item);
  463. break;
  464. case TypeCode.Int16:
  465. if (array.Length > 2)
  466. BlockWrite (writer, array, 2);
  467. else
  468. foreach (short item in (short[]) array)
  469. writer.Write (item);
  470. break;
  471. case TypeCode.Int32:
  472. if (array.Length > 2)
  473. BlockWrite (writer, array, 4);
  474. else
  475. foreach (int item in (int[]) array)
  476. writer.Write (item);
  477. break;
  478. case TypeCode.Int64:
  479. if (array.Length > 2)
  480. BlockWrite (writer, array, 8);
  481. else
  482. foreach (long item in (long[]) array)
  483. writer.Write (item);
  484. break;
  485. case TypeCode.SByte:
  486. if (array.Length > 2)
  487. BlockWrite (writer, array, 1);
  488. else
  489. foreach (sbyte item in (sbyte[]) array)
  490. writer.Write (item);
  491. break;
  492. case TypeCode.Single:
  493. if (array.Length > 2)
  494. BlockWrite (writer, array, 4);
  495. else
  496. foreach (float item in (float[]) array)
  497. writer.Write (item);
  498. break;
  499. case TypeCode.UInt16:
  500. if (array.Length > 2)
  501. BlockWrite (writer, array, 2);
  502. else
  503. foreach (ushort item in (ushort[]) array)
  504. writer.Write (item);
  505. break;
  506. case TypeCode.UInt32:
  507. if (array.Length > 2)
  508. BlockWrite (writer, array, 4);
  509. else
  510. foreach (uint item in (uint[]) array)
  511. writer.Write (item);
  512. break;
  513. case TypeCode.UInt64:
  514. if (array.Length > 2)
  515. BlockWrite (writer, array, 8);
  516. else
  517. foreach (ulong item in (ulong[]) array)
  518. writer.Write (item);
  519. break;
  520. case TypeCode.String:
  521. foreach (string item in (string[]) array)
  522. writer.Write (item);
  523. break;
  524. default:
  525. if (elementType == typeof (TimeSpan)) {
  526. foreach (TimeSpan item in (TimeSpan[]) array)
  527. writer.Write (item.Ticks);
  528. }
  529. else
  530. throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);
  531. break;
  532. }
  533. }
  534. private void BlockWrite (BinaryWriter writer, Array array, int dataSize)
  535. {
  536. int totalSize = Buffer.ByteLength (array);
  537. if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))
  538. arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];
  539. int pos = 0;
  540. while (totalSize > 0) {
  541. int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;
  542. Buffer.BlockCopy (array, pos, arrayBuffer, 0, size);
  543. if (!BitConverter.IsLittleEndian && dataSize > 1)
  544. BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);
  545. writer.Write (arrayBuffer, 0, size);
  546. totalSize -= size;
  547. pos += size;
  548. }
  549. }
  550. private void WriteSingleDimensionArrayElements (BinaryWriter writer, Array array, Type elementType)
  551. {
  552. int numNulls = 0;
  553. foreach (object val in array)
  554. {
  555. if (val != null && numNulls > 0)
  556. {
  557. WriteNullFiller (writer, numNulls);
  558. WriteValue (writer, elementType, val);
  559. numNulls = 0;
  560. }
  561. else if (val == null)
  562. numNulls++;
  563. else
  564. WriteValue (writer, elementType, val);
  565. }
  566. if (numNulls > 0)
  567. WriteNullFiller (writer, numNulls);
  568. }
  569. private void WriteNullFiller (BinaryWriter writer, int numNulls)
  570. {
  571. if (numNulls == 1) {
  572. writer.Write ((byte) BinaryElement.NullValue);
  573. }
  574. else if (numNulls == 2) {
  575. writer.Write ((byte) BinaryElement.NullValue);
  576. writer.Write ((byte) BinaryElement.NullValue);
  577. }
  578. else if (numNulls <= byte.MaxValue) {
  579. writer.Write ((byte) BinaryElement.ArrayFiller8b);
  580. writer.Write ((byte) numNulls);
  581. }
  582. else {
  583. writer.Write ((byte) BinaryElement.ArrayFiller32b);
  584. writer.Write (numNulls);
  585. }
  586. }
  587. private void WriteObjectReference (BinaryWriter writer, long id)
  588. {
  589. writer.Write ((byte) BinaryElement.ObjectReference);
  590. writer.Write ((int)id);
  591. }
  592. public void WriteValue (BinaryWriter writer, Type valueType, object val)
  593. {
  594. if (val == null)
  595. {
  596. BinaryCommon.CheckSerializable (valueType, _surrogateSelector, _context);
  597. writer.Write ((byte) BinaryElement.NullValue);
  598. }
  599. else if (BinaryCommon.IsPrimitive(val.GetType()))
  600. {
  601. if (!BinaryCommon.IsPrimitive(valueType))
  602. {
  603. // It is a boxed primitive type value
  604. writer.Write ((byte) BinaryElement.BoxedPrimitiveTypeValue);
  605. WriteTypeSpec (writer, val.GetType());
  606. }
  607. WritePrimitiveValue (writer, val);
  608. }
  609. else if (valueType.IsValueType)
  610. {
  611. // Value types are written embedded in the containing object
  612. WriteObjectInstance (writer, val, true);
  613. }
  614. else if (val is string)
  615. {
  616. // Strings are written embedded, unless already registered
  617. bool firstTime;
  618. long id = _idGenerator.GetId (val, out firstTime);
  619. if (firstTime) WriteObjectInstance (writer, val, false);
  620. else WriteObjectReference (writer, id);
  621. }
  622. else
  623. {
  624. // It is a reference type. Write a forward reference and queue the
  625. // object to the pending object list (unless already written).
  626. bool firstTime;
  627. long id = _idGenerator.GetId (val, out firstTime);
  628. if (firstTime) _pendingObjects.Enqueue (val);
  629. WriteObjectReference (writer, id);
  630. }
  631. }
  632. private void WriteString (BinaryWriter writer, long id, string str)
  633. {
  634. writer.Write ((byte) BinaryElement.String);
  635. writer.Write ((int)id);
  636. writer.Write (str);
  637. }
  638. public int WriteAssembly (BinaryWriter writer, Assembly assembly)
  639. {
  640. return WriteAssemblyName (writer, assembly.FullName);
  641. }
  642. public int WriteAssemblyName (BinaryWriter writer, string assembly)
  643. {
  644. if (assembly == ObjectWriter.CorlibAssemblyName) return -1;
  645. bool firstTime;
  646. int id = RegisterAssembly (assembly, out firstTime);
  647. if (!firstTime) return id;
  648. writer.Write ((byte) BinaryElement.Assembly);
  649. writer.Write (id);
  650. if (_assemblyFormat == FormatterAssemblyStyle.Full)
  651. writer.Write (assembly);
  652. else {
  653. int i = assembly.IndexOf (',');
  654. if (i != -1) assembly = assembly.Substring (0, i);
  655. writer.Write (assembly);
  656. }
  657. return id;
  658. }
  659. public int GetAssemblyId (Assembly assembly)
  660. {
  661. return GetAssemblyNameId (assembly.FullName);
  662. }
  663. public int GetAssemblyNameId (string assembly)
  664. {
  665. return (int)_assemblyCache[assembly];
  666. }
  667. private int RegisterAssembly (string assembly, out bool firstTime)
  668. {
  669. if (_assemblyCache.ContainsKey (assembly))
  670. {
  671. firstTime = false;
  672. return (int)_assemblyCache[assembly];
  673. }
  674. else
  675. {
  676. int id = (int)_idGenerator.GetId (0, out firstTime);
  677. _assemblyCache.Add (assembly, id);
  678. return id;
  679. }
  680. }
  681. public static void WritePrimitiveValue (BinaryWriter writer, object value)
  682. {
  683. Type type = value.GetType();
  684. switch (Type.GetTypeCode (type))
  685. {
  686. case TypeCode.Boolean:
  687. writer.Write ((bool)value);
  688. break;
  689. case TypeCode.Byte:
  690. writer.Write ((byte) value);
  691. break;
  692. case TypeCode.Char:
  693. writer.Write ((char) value);
  694. break;
  695. case TypeCode.DateTime:
  696. writer.Write ( ((DateTime)value).Ticks);
  697. break;
  698. case TypeCode.Decimal:
  699. writer.Write (((decimal) value).ToString (CultureInfo.InvariantCulture));
  700. break;
  701. case TypeCode.Double:
  702. writer.Write ((double) value);
  703. break;
  704. case TypeCode.Int16:
  705. writer.Write ((short) value);
  706. break;
  707. case TypeCode.Int32:
  708. writer.Write ((int) value);
  709. break;
  710. case TypeCode.Int64:
  711. writer.Write ((long) value);
  712. break;
  713. case TypeCode.SByte:
  714. writer.Write ((sbyte) value);
  715. break;
  716. case TypeCode.Single:
  717. writer.Write ((float) value);
  718. break;
  719. case TypeCode.UInt16:
  720. writer.Write ((ushort) value);
  721. break;
  722. case TypeCode.UInt32:
  723. writer.Write ((uint) value);
  724. break;
  725. case TypeCode.UInt64:
  726. writer.Write ((ulong) value);
  727. break;
  728. case TypeCode.String:
  729. writer.Write ((string) value);
  730. break;
  731. default:
  732. if (type == typeof (TimeSpan))
  733. writer.Write (((TimeSpan)value).Ticks);
  734. else
  735. throw new NotSupportedException ("Unsupported primitive type: " + value.GetType().FullName);
  736. break;
  737. }
  738. }
  739. public static void WriteTypeCode (BinaryWriter writer, Type type)
  740. {
  741. writer.Write ((byte) GetTypeTag (type));
  742. }
  743. public static TypeTag GetTypeTag (Type type)
  744. {
  745. if (type == typeof (string)) {
  746. return TypeTag.String;
  747. }
  748. else if (BinaryCommon.IsPrimitive (type)) {
  749. return TypeTag.PrimitiveType;
  750. }
  751. else if (type == typeof (object)) {
  752. return TypeTag.ObjectType;
  753. }
  754. else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (object)) {
  755. return TypeTag.ArrayOfObject;
  756. }
  757. else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (string)){
  758. return TypeTag.ArrayOfString;
  759. }
  760. else if (type.IsArray && type.GetArrayRank() == 1 && BinaryCommon.IsPrimitive(type.GetElementType())) {
  761. return TypeTag.ArrayOfPrimitiveType;
  762. }
  763. else if (type.Assembly == CorlibAssembly) {
  764. return TypeTag.RuntimeType;
  765. }
  766. else
  767. return TypeTag.GenericType;
  768. }
  769. public void WriteTypeSpec (BinaryWriter writer, Type type)
  770. {
  771. // WARNING Keep in sync with EmitWriteTypeSpec
  772. switch (GetTypeTag (type))
  773. {
  774. case TypeTag.PrimitiveType:
  775. writer.Write (BinaryCommon.GetTypeCode (type));
  776. break;
  777. case TypeTag.RuntimeType:
  778. writer.Write (type.FullName);
  779. break;
  780. case TypeTag.GenericType:
  781. writer.Write (type.FullName);
  782. writer.Write ((int)GetAssemblyId (type.Assembly));
  783. break;
  784. case TypeTag.ArrayOfPrimitiveType:
  785. writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
  786. break;
  787. default:
  788. // Type spec not needed
  789. break;
  790. }
  791. }
  792. }
  793. }