Implement mono_gc_alloc_fixed on Boehm to be uncollectable. This matches SGen behavio...
[mono.git] / mcs / class / referencesource / mscorlib / system / resources / resourcewriter.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  ResourceWriter
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: Default way to write strings to a CLR resource 
14 ** file.
15 **
16 ** 
17 ===========================================================*/
18 namespace System.Resources {
19     using System;
20     using System.IO;
21     using System.Text;
22     using System.Collections;
23     using System.Collections.Generic;
24 #if FEATURE_SERIALIZATION
25     using System.Runtime.Serialization;
26     using System.Runtime.Serialization.Formatters.Binary;
27 #endif // FEATURE_SERIALIZATION
28     using System.Globalization;
29     using System.Runtime.Versioning;
30     using System.Diagnostics.Contracts;
31     using System.Security;
32     using System.Security.Permissions;
33
34     // Generates a binary .resources file in the system default format 
35     // from name and value pairs.  Create one with a unique file name,
36     // call AddResource() at least once, then call Generate() to write
37     // the .resources file to disk, then call Close() to close the file.
38     // 
39     // The resources generally aren't written out in the same order 
40     // they were added.
41     // 
42     // See the RuntimeResourceSet overview for details on the system 
43     // default file format.
44     // 
45 [System.Runtime.InteropServices.ComVisible(true)]
46     public sealed class ResourceWriter : IResourceWriter
47     {
48
49         private Func<Type, String> typeConverter;
50
51         // Set this delegate to allow multi-targeting for .resources files.
52         public Func<Type, String> TypeNameConverter
53         {
54             get
55             {
56                 return typeConverter;
57             }
58             set
59             {
60                 typeConverter = value;
61             }
62         }
63         
64         // For cases where users can't create an instance of the deserialized 
65         // type in memory, and need to pass us serialized blobs instead.
66         // LocStudio's managed code parser will do this in some cases.
67         private class PrecannedResource
68         {
69             internal String TypeName;
70             internal byte[] Data;
71
72             internal PrecannedResource(String typeName, byte[] data)
73             {
74                 TypeName = typeName;
75                 Data = data;
76             }
77         }
78
79         private class StreamWrapper
80         {
81             internal Stream m_stream;
82             internal bool m_closeAfterWrite;
83
84             internal StreamWrapper(Stream s, bool closeAfterWrite)
85             {
86                 m_stream = s;
87                 m_closeAfterWrite = closeAfterWrite;
88             }
89         }
90
91         // An initial size for our internal sorted list, to avoid extra resizes.
92         private const int _ExpectedNumberOfResources = 1000;
93         private const int AverageNameSize = 20 * 2;  // chars in little endian Unicode
94         private const int AverageValueSize = 40;
95
96         private Dictionary<String, Object> _resourceList;
97         internal Stream _output;
98         private Dictionary<String, Object> _caseInsensitiveDups;
99         private Dictionary<String, PrecannedResource> _preserializedData;
100         private const int _DefaultBufferSize = 4096;
101
102         [ResourceExposure(ResourceScope.Machine)]
103         [ResourceConsumption(ResourceScope.Machine)]        
104         public ResourceWriter(String fileName)
105         {
106             if (fileName==null)
107                 throw new ArgumentNullException("fileName");
108             Contract.EndContractBlock();
109             _output = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
110             _resourceList = new Dictionary<String, Object>(_ExpectedNumberOfResources, FastResourceComparer.Default);
111             _caseInsensitiveDups = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
112         }
113     
114         public ResourceWriter(Stream stream)
115         {
116             if (stream==null)
117                 throw new ArgumentNullException("stream");
118             if (!stream.CanWrite)
119                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable"));
120             Contract.EndContractBlock();
121             _output = stream;
122             _resourceList = new Dictionary<String, Object>(_ExpectedNumberOfResources, FastResourceComparer.Default);
123             _caseInsensitiveDups = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
124         }
125     
126         // Adds a string resource to the list of resources to be written to a file.
127         // They aren't written until Generate() is called.
128         // 
129         public void AddResource(String name, String value)
130         {
131             if (name==null)
132                 throw new ArgumentNullException("name");
133             Contract.EndContractBlock();
134             if (_resourceList == null)
135                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
136
137             // Check for duplicate resources whose names vary only by case.
138             _caseInsensitiveDups.Add(name, null);
139             _resourceList.Add(name, value);
140         }
141         
142         // Adds a resource of type Object to the list of resources to be 
143         // written to a file.  They aren't written until Generate() is called.
144         // 
145         public void AddResource(String name, Object value)
146         {
147             if (name==null)
148                 throw new ArgumentNullException("name");
149             Contract.EndContractBlock();
150             if (_resourceList == null)
151                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
152
153             // needed for binary compat
154             if (value != null && value is Stream)
155             {
156                 AddResourceInternal(name, (Stream)value, false);
157             }
158             else
159             {
160                 // Check for duplicate resources whose names vary only by case.
161                 _caseInsensitiveDups.Add(name, null);
162                 _resourceList.Add(name, value);
163             }
164         }
165
166         // Adds a resource of type Stream to the list of resources to be 
167         // written to a file.  They aren't written until Generate() is called.
168         // Doesn't close the Stream when done.
169         //
170         public void AddResource(String name, Stream value)
171         {
172             if (name == null)
173                 throw new ArgumentNullException("name");
174             Contract.EndContractBlock();
175             if (_resourceList == null)
176                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
177
178             AddResourceInternal(name, value, false);
179         }
180
181         // Adds a resource of type Stream to the list of resources to be 
182         // written to a file.  They aren't written until Generate() is called.
183         // closeAfterWrite parameter indicates whether to close the stream when done.
184         // 
185         public void AddResource(String name, Stream value, bool closeAfterWrite)
186         {
187             if (name == null)
188                 throw new ArgumentNullException("name");
189             Contract.EndContractBlock();
190             if (_resourceList == null)
191                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
192
193             AddResourceInternal(name, value, closeAfterWrite);
194         }
195
196         private void AddResourceInternal(String name, Stream value, bool closeAfterWrite)
197         {
198             if (value == null)
199             {
200                 // Check for duplicate resources whose names vary only by case.
201                 _caseInsensitiveDups.Add(name, null);
202                 _resourceList.Add(name, value);
203             }
204             else
205             {
206                 // make sure the Stream is seekable
207                 if (!value.CanSeek)
208                     throw new ArgumentException(Environment.GetResourceString("NotSupported_UnseekableStream"));
209
210                 // Check for duplicate resources whose names vary only by case.
211                 _caseInsensitiveDups.Add(name, null);
212                 _resourceList.Add(name, new StreamWrapper(value, closeAfterWrite));
213             }
214         }
215
216         // Adds a named byte array as a resource to the list of resources to 
217         // be written to a file. They aren't written until Generate() is called.
218         // 
219         public void AddResource(String name, byte[] value)
220         {
221             if (name==null)
222                 throw new ArgumentNullException("name");
223             Contract.EndContractBlock();
224             if (_resourceList == null)
225                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
226
227             // Check for duplicate resources whose names vary only by case.
228             _caseInsensitiveDups.Add(name, null);
229             _resourceList.Add(name, value);
230         }
231         
232         public void AddResourceData(String name, String typeName, byte[] serializedData)
233         {
234             if (name == null)
235                 throw new ArgumentNullException("name");
236             if (typeName == null)
237                 throw new ArgumentNullException("typeName");
238             if (serializedData == null)
239                 throw new ArgumentNullException("serializedData");
240             Contract.EndContractBlock();
241             if (_resourceList == null)
242                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
243
244             // Check for duplicate resources whose names vary only by case.
245             _caseInsensitiveDups.Add(name, null);
246             if (_preserializedData == null)
247                 _preserializedData = new Dictionary<String, PrecannedResource>(FastResourceComparer.Default);
248
249             _preserializedData.Add(name, new PrecannedResource(typeName, serializedData));
250         }
251
252
253         // Closes the output stream.
254         public void Close()
255         {
256             Dispose(true);
257         }
258
259         private void Dispose(bool disposing)
260         {
261             if (disposing) {
262                 if (_resourceList != null) {
263                     Generate();
264                 }
265                 if (_output != null) {
266                     _output.Close();
267                 }
268             }
269             _output = null;
270             _caseInsensitiveDups = null;
271             // _resourceList is set to null by Generate.
272         }
273
274         public void Dispose()
275         {
276             Dispose(true);
277         }
278
279         // After calling AddResource, Generate() writes out all resources to the 
280         // output stream in the system default format.
281         // If an exception occurs during object serialization or during IO,
282         // the .resources file is closed and deleted, since it is most likely
283         // invalid.
284         [SecuritySafeCritical]  // Asserts permission to create & delete a temp file.
285         public void Generate()
286         {
287             if (_resourceList == null)
288                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
289
290             BinaryWriter bw = new BinaryWriter(_output, Encoding.UTF8);
291             List<String> typeNames = new List<String>();
292                 
293             // Write out the ResourceManager header
294             // Write out magic number
295             bw.Write(ResourceManager.MagicNumber);
296                 
297             // Write out ResourceManager header version number
298             bw.Write(ResourceManager.HeaderVersionNumber);
299
300             MemoryStream resMgrHeaderBlob = new MemoryStream(240);
301             BinaryWriter resMgrHeaderPart = new BinaryWriter(resMgrHeaderBlob);
302
303             // Write out class name of IResourceReader capable of handling 
304             // this file.
305             resMgrHeaderPart.Write(MultitargetingHelpers.GetAssemblyQualifiedName(typeof(ResourceReader),typeConverter));
306
307             // Write out class name of the ResourceSet class best suited to
308             // handling this file.
309             // This needs to be the same even with multi-targeting. It's the 
310             // full name -- not the ----sembly qualified name.
311             resMgrHeaderPart.Write(ResourceManager.ResSetTypeName);
312             resMgrHeaderPart.Flush();
313
314             // Write number of bytes to skip over to get past ResMgr header
315             bw.Write((int)resMgrHeaderBlob.Length);
316
317             // Write the rest of the ResMgr header
318             bw.Write(resMgrHeaderBlob.GetBuffer(), 0, (int)resMgrHeaderBlob.Length);
319             // End ResourceManager header
320
321
322             // Write out the RuntimeResourceSet header
323             // Version number
324             bw.Write(RuntimeResourceSet.Version);
325 #if RESOURCE_FILE_FORMAT_DEBUG
326             // Write out a tag so we know whether to enable or disable 
327             // debugging support when reading the file.
328             bw.Write("***DEBUG***");
329 #endif
330
331             // number of resources
332             int numResources = _resourceList.Count;
333             if (_preserializedData != null)
334                 numResources += _preserializedData.Count;
335             bw.Write(numResources);
336                 
337             // Store values in temporary streams to write at end of file.
338             int[] nameHashes = new int[numResources];
339             int[] namePositions = new int[numResources];
340             int curNameNumber = 0;
341             MemoryStream nameSection = new MemoryStream(numResources * AverageNameSize);
342             BinaryWriter names = new BinaryWriter(nameSection, Encoding.Unicode);
343
344             // The data section can be very large, and worse yet, we can grow the byte[] used
345             // for the data section repeatedly.  When using large resources like ~100 images,
346             // this can lead to both a fragmented large object heap as well as allocating about
347             // 2-3x of our storage needs in extra overhead.  Using a temp file can avoid this.
348             // Assert permission to get a temp file name, which requires two permissions.
349             // Additionally, we may be running under an account that doesn't have permission to
350             // write to the temp directory (enforced via a Windows ACL).  Fall back to a MemoryStream.
351             Stream dataSection = null;  // Either a FileStream or a MemoryStream
352             String tempFile = null;
353 #if FEATURE_MONO_CAS
354             PermissionSet permSet = new PermissionSet(PermissionState.None);
355             permSet.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted));
356             permSet.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
357 #endif
358             try {
359 #if FEATURE_MONO_CAS
360                 permSet.Assert();
361 #endif
362                 tempFile = Path.GetTempFileName();
363                 File.SetAttributes(tempFile, FileAttributes.Temporary | FileAttributes.NotContentIndexed);
364                 // Explicitly opening with FileOptions.DeleteOnClose to avoid complicated File.Delete
365                 // (safe from ----s w/ antivirus software, etc)
366                 dataSection = new FileStream(tempFile, FileMode.Open, FileAccess.ReadWrite, FileShare.Read,
367                                              4096, FileOptions.DeleteOnClose | FileOptions.SequentialScan);
368             }
369             catch (UnauthorizedAccessException) {
370                 // In case we're running under an account that can't access a temp directory.
371                 dataSection = new MemoryStream();
372             }
373             catch (IOException) {
374                 // In case Path.GetTempFileName fails because no unique file names are available
375                 dataSection = new MemoryStream();
376             }
377             finally {
378 #if FEATURE_MONO_CAS
379                 PermissionSet.RevertAssert();
380 #endif
381             }
382
383             using(dataSection) {
384                 BinaryWriter data = new BinaryWriter(dataSection, Encoding.UTF8);
385 #if FEATURE_SERIALIZATION
386                 IFormatter objFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
387 #endif // FEATURE_SERIALIZATION
388
389 #if RESOURCE_FILE_FORMAT_DEBUG
390                 // Write NAMES right before the names section.
391                 names.Write(new byte[] { (byte) 'N', (byte) 'A', (byte) 'M', (byte) 'E', (byte) 'S', (byte) '-', (byte) '-', (byte) '>'});
392             
393                 // Write DATA at the end of the name table section.
394                 data.Write(new byte[] { (byte) 'D', (byte) 'A', (byte) 'T', (byte) 'A', (byte) '-', (byte) '-', (byte)'-', (byte)'>'});
395 #endif
396
397                 // We've stored our resources internally in a Hashtable, which 
398                 // makes no guarantees about the ordering while enumerating.  
399                 // While we do our own sorting of the resource names based on their
400                 // hash values, that's only sorting the nameHashes and namePositions
401                 // arrays.  That's all that is strictly required for correctness,
402                 // but for ease of generating a patch in the future that 
403                 // modifies just .resources files, we should re-sort them.
404
405                 SortedList sortedResources = new SortedList(_resourceList, FastResourceComparer.Default);
406                 if (_preserializedData != null) {
407                     foreach (KeyValuePair<String, PrecannedResource> entry in _preserializedData)
408                         sortedResources.Add(entry.Key, entry.Value);
409                 }
410
411                 IDictionaryEnumerator items = sortedResources.GetEnumerator();
412                 // Write resource name and position to the file, and the value
413                 // to our temporary buffer.  Save Type as well.
414                 while (items.MoveNext()) {
415                     nameHashes[curNameNumber] = FastResourceComparer.HashFunction((String)items.Key);
416                     namePositions[curNameNumber++] = (int)names.Seek(0, SeekOrigin.Current);
417                     names.Write((String)items.Key); // key
418                     names.Write((int)data.Seek(0, SeekOrigin.Current)); // virtual offset of value.
419 #if RESOURCE_FILE_FORMAT_DEBUG
420                     names.Write((byte) '*');
421 #endif
422                     Object value = items.Value;
423                     ResourceTypeCode typeCode = FindTypeCode(value, typeNames);
424
425                     // Write out type code
426                     Write7BitEncodedInt(data, (int)typeCode);
427
428                     // Write out value
429                     PrecannedResource userProvidedResource = value as PrecannedResource;
430                     if (userProvidedResource != null) {
431                         data.Write(userProvidedResource.Data);
432                     }
433                     else {
434 #if FEATURE_SERIALIZATION
435                         WriteValue(typeCode, value, data, objFormatter);
436 #else 
437                         WriteValue(typeCode, value, data);
438 #endif
439                     }
440
441 #if RESOURCE_FILE_FORMAT_DEBUG
442                     data.Write(new byte[] { (byte) 'S', (byte) 'T', (byte) 'O', (byte) 'P'});
443 #endif
444                 }
445
446                 // At this point, the ResourceManager header has been written.
447                 // Finish RuntimeResourceSet header
448                 //   Write size & contents of class table
449                 bw.Write(typeNames.Count);
450                 for (int i = 0; i < typeNames.Count; i++)
451                     bw.Write(typeNames[i]);
452
453                 // Write out the name-related items for lookup.
454                 //  Note that the hash array and the namePositions array must
455                 //  be sorted in parallel.
456                 Array.Sort(nameHashes, namePositions);
457
458                 //  Prepare to write sorted name hashes (alignment fixup)
459                 //   Note: For 64-bit machines, these MUST be aligned on 8 byte 
460                 //   boundaries!  Pointers on IA64 must be aligned!  And we'll
461                 //   run faster on X86 machines too.
462                 bw.Flush();
463                 int alignBytes = ((int)bw.BaseStream.Position) & 7;
464                 if (alignBytes > 0) {
465                     for (int i = 0; i < 8 - alignBytes; i++)
466                         bw.Write("PAD"[i % 3]);
467                 }
468
469                 //  Write out sorted name hashes.
470                 //   Align to 8 bytes.
471                 Contract.Assert((bw.BaseStream.Position & 7) == 0, "ResourceWriter: Name hashes array won't be 8 byte aligned!  Ack!");
472 #if RESOURCE_FILE_FORMAT_DEBUG
473                 bw.Write(new byte[] { (byte) 'H', (byte) 'A', (byte) 'S', (byte) 'H', (byte) 'E', (byte) 'S', (byte) '-', (byte) '>'} );
474 #endif
475                 foreach (int hash in nameHashes)
476                     bw.Write(hash);
477 #if RESOURCE_FILE_FORMAT_DEBUG
478                 Console.Write("Name hashes: ");
479                 foreach(int hash in nameHashes)
480                     Console.Write(hash.ToString("x")+"  ");
481                 Console.WriteLine();
482 #endif
483
484                 //  Write relative positions of all the names in the file.
485                 //   Note: this data is 4 byte aligned, occuring immediately 
486                 //   after the 8 byte aligned name hashes (whose length may 
487                 //   potentially be odd).
488                 Contract.Assert((bw.BaseStream.Position & 3) == 0, "ResourceWriter: Name positions array won't be 4 byte aligned!  Ack!");
489 #if RESOURCE_FILE_FORMAT_DEBUG
490                 bw.Write(new byte[] { (byte) 'P', (byte) 'O', (byte) 'S', (byte) '-', (byte) '-', (byte) '-', (byte) '-', (byte) '>' } );
491 #endif
492                 foreach (int pos in namePositions)
493                     bw.Write(pos);
494 #if RESOURCE_FILE_FORMAT_DEBUG
495                 Console.Write("Name positions: ");
496                 foreach(int pos in namePositions)
497                     Console.Write(pos.ToString("x")+"  ");
498                 Console.WriteLine();
499 #endif
500
501                 // Flush all BinaryWriters to their underlying streams.
502                 bw.Flush();
503                 names.Flush();
504                 data.Flush();
505
506                 // Write offset to data section
507                 int startOfDataSection = (int)(bw.Seek(0, SeekOrigin.Current) + nameSection.Length);
508                 startOfDataSection += 4;  // We're writing an int to store this data, adding more bytes to the header
509                 BCLDebug.Log("RESMGRFILEFORMAT", "Generate: start of DataSection: 0x" + startOfDataSection.ToString("x", CultureInfo.InvariantCulture) + "  nameSection length: " + nameSection.Length);
510                 bw.Write(startOfDataSection);
511
512                 // Write name section.
513                 bw.Write(nameSection.GetBuffer(), 0, (int)nameSection.Length);
514                 names.Close();
515
516                 // Write data section.
517                 Contract.Assert(startOfDataSection == bw.Seek(0, SeekOrigin.Current), "ResourceWriter::Generate - start of data section is wrong!");
518                 dataSection.Position = 0;
519                 dataSection.CopyTo(bw.BaseStream);
520                 data.Close();
521             } // using(dataSection)  <--- Closes dataSection, which was opened w/ FileOptions.DeleteOnClose
522             bw.Flush();
523
524             // Indicate we've called Generate
525             _resourceList = null;
526         }
527
528         // Finds the ResourceTypeCode for a type, or adds this type to the
529         // types list.
530         private ResourceTypeCode FindTypeCode(Object value, List<String> types)
531         {
532             if (value == null)
533                 return ResourceTypeCode.Null;
534
535             Type type = value.GetType();
536             if (type == typeof(String))
537                 return ResourceTypeCode.String;
538             else if (type == typeof(Int32))
539                 return ResourceTypeCode.Int32;
540             else if (type == typeof(Boolean))
541                 return ResourceTypeCode.Boolean;
542             else if (type == typeof(Char))
543                 return ResourceTypeCode.Char;
544             else if (type == typeof(Byte))
545                 return ResourceTypeCode.Byte;
546             else if (type == typeof(SByte))
547                 return ResourceTypeCode.SByte;
548             else if (type == typeof(Int16))
549                 return ResourceTypeCode.Int16;
550             else if (type == typeof(Int64))
551                 return ResourceTypeCode.Int64;
552             else if (type == typeof(UInt16))
553                 return ResourceTypeCode.UInt16;
554             else if (type == typeof(UInt32))
555                 return ResourceTypeCode.UInt32;
556             else if (type == typeof(UInt64))
557                 return ResourceTypeCode.UInt64;
558             else if (type == typeof(Single))
559                 return ResourceTypeCode.Single;
560             else if (type == typeof(Double))
561                 return ResourceTypeCode.Double;
562             else if (type == typeof (Decimal))
563                 return ResourceTypeCode.Decimal;
564             else if (type == typeof(DateTime))
565                 return ResourceTypeCode.DateTime;
566             else if (type == typeof(TimeSpan))
567                 return ResourceTypeCode.TimeSpan;
568             else if (type == typeof(byte[]))
569                 return ResourceTypeCode.ByteArray;
570             else if (type == typeof(StreamWrapper))
571                 return ResourceTypeCode.Stream;
572
573             
574             // This is a user type, or a precanned resource.  Find type 
575             // table index.  If not there, add new element.
576             String typeName;
577             if (type == typeof(PrecannedResource)) {
578                 typeName = ((PrecannedResource)value).TypeName;
579                 if (typeName.StartsWith("ResourceTypeCode.", StringComparison.Ordinal)) {
580                     typeName = typeName.Substring(17);  // Remove through '.'
581                     ResourceTypeCode typeCode = (ResourceTypeCode)Enum.Parse(typeof(ResourceTypeCode), typeName);
582                     return typeCode;
583                 }
584             }
585             else 
586             {
587                 typeName = MultitargetingHelpers.GetAssemblyQualifiedName(type, typeConverter);
588             }
589
590             int typeIndex = types.IndexOf(typeName);
591             if (typeIndex == -1) {
592                 typeIndex = types.Count;
593                 types.Add(typeName);
594             }
595
596             return (ResourceTypeCode)(typeIndex + ResourceTypeCode.StartOfUserTypes);
597         }
598
599 #if FEATURE_SERIALIZATION
600         private void WriteValue(ResourceTypeCode typeCode, Object value, BinaryWriter writer, IFormatter objFormatter)
601 #else 
602         private void WriteValue(ResourceTypeCode typeCode, Object value, BinaryWriter writer)                                                         
603 #endif // FEATURE_SERIALIZATION
604         {
605             Contract.Requires(writer != null);
606
607             switch(typeCode) {
608             case ResourceTypeCode.Null:
609                 break;
610
611             case ResourceTypeCode.String:
612                 writer.Write((String) value);
613                 break;
614
615             case ResourceTypeCode.Boolean:
616                 writer.Write((bool) value);
617                 break;
618
619             case ResourceTypeCode.Char:
620                 writer.Write((UInt16) (char) value);
621                 break;
622
623             case ResourceTypeCode.Byte:
624                 writer.Write((byte) value);
625                 break;
626
627             case ResourceTypeCode.SByte:
628                 writer.Write((sbyte) value);
629                 break;
630                 
631             case ResourceTypeCode.Int16:
632                 writer.Write((Int16) value);
633                 break;
634                 
635             case ResourceTypeCode.UInt16:
636                 writer.Write((UInt16) value);
637                 break;
638
639             case ResourceTypeCode.Int32:
640                 writer.Write((Int32) value);
641                 break;
642                 
643             case ResourceTypeCode.UInt32:
644                 writer.Write((UInt32) value);
645                 break;
646
647             case ResourceTypeCode.Int64:
648                 writer.Write((Int64) value);
649                 break;
650                 
651             case ResourceTypeCode.UInt64:
652                 writer.Write((UInt64) value);
653                 break;
654
655             case ResourceTypeCode.Single:
656                 writer.Write((Single) value);
657                 break;
658                 
659             case ResourceTypeCode.Double:
660                 writer.Write((Double) value);
661                 break;
662
663             case ResourceTypeCode.Decimal:
664                 writer.Write((Decimal) value);
665                 break;
666
667             case ResourceTypeCode.DateTime:
668                 // Use DateTime's ToBinary & FromBinary.
669                 Int64 data = ((DateTime) value).ToBinary();
670                 writer.Write(data);
671                 break;
672                 
673             case ResourceTypeCode.TimeSpan:
674                 writer.Write(((TimeSpan) value).Ticks);
675                 break;
676
677             // Special Types
678             case ResourceTypeCode.ByteArray:
679                 {
680                     byte[] bytes = (byte[]) value;
681                     writer.Write(bytes.Length);
682                     writer.Write(bytes, 0, bytes.Length);
683                     break;
684                 }
685
686             case ResourceTypeCode.Stream:
687                 {
688                     StreamWrapper sw = (StreamWrapper)value;
689                     if (sw.m_stream.GetType() == typeof(MemoryStream))
690                     {
691                         MemoryStream ms = (MemoryStream)sw.m_stream;
692                         if (ms.Length > Int32.MaxValue)
693                             throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
694                         int offset, len;
695                         ms.InternalGetOriginAndLength(out offset, out len);
696                         byte[] bytes = ms.InternalGetBuffer();
697                         writer.Write(len);
698                         writer.Write(bytes, offset, len);
699                     }
700                     else 
701                     {
702                         Stream s = sw.m_stream;
703                         // we've already verified that the Stream is seekable
704                         if (s.Length > Int32.MaxValue)
705                             throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
706
707                         s.Position = 0;
708                         writer.Write((int)s.Length);
709                         byte[] buffer = new byte[_DefaultBufferSize];
710                         int read = 0;
711                         while ((read = s.Read(buffer, 0, buffer.Length)) != 0)
712                         {
713                             writer.Write(buffer, 0, read);
714                         }
715                         if (sw.m_closeAfterWrite)
716                         {
717                             s.Close();
718                         }
719                     }
720                     break;
721                 }
722
723             default:
724                 Contract.Assert(typeCode >= ResourceTypeCode.StartOfUserTypes, String.Format(CultureInfo.InvariantCulture, "ResourceReader: Unsupported ResourceTypeCode in .resources file!  {0}", typeCode));
725 #if FEATURE_SERIALIZATION
726                 objFormatter.Serialize(writer.BaseStream, value);
727                 break;
728 #else
729                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
730 #endif // FEATURE_SERIALIZATION  
731             }
732         }
733
734         private static void Write7BitEncodedInt(BinaryWriter store, int value) {
735             Contract.Requires(store != null);
736             // Write out an int 7 bits at a time.  The high bit of the byte,
737             // when on, tells reader to continue reading more bytes.
738             uint v = (uint) value;   // support negative numbers
739             while (v >= 0x80) {
740                 store.Write((byte) (v | 0x80));
741                 v >>= 7;
742             }
743             store.Write((byte)v);
744         }
745     }
746 }