1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  BinaryWriter
9 **
10 ** <OWNER>gpaperin</OWNER>
11 **
12 ** Purpose: Provides a way to write primitives types in
13 ** binary from a Stream, while also supporting writing Strings
14 ** in a particular encoding.
15 **
16 **
17 ===========================================================*/
18 using System;
19 using System.Runtime;
20 using System.Runtime.Serialization;
21 using System.Text;
22 using System.Diagnostics.Contracts;
23 
24 namespace System.IO {
25     // This abstract base class represents a writer that can write
26     // primitives to an arbitrary stream. A subclass can override methods to
27     // give unique encodings.
28     //
29     [Serializable]
30 [System.Runtime.InteropServices.ComVisible(true)]
31     public class BinaryWriter : IDisposable
32     {
33         public static readonly BinaryWriter Null = new BinaryWriter();
34 
35         protected Stream OutStream;
36         private byte[] _buffer;    // temp space for writing primitives to.
37         private Encoding _encoding;
38         private Encoder _encoder;
39 
40         [OptionalField]  // New in .NET FX 4.5.  False is the right default value.
41         private bool _leaveOpen;
42 
43         // This field should never have been serialized and has not been used since before v2.0.
44         // However, this type is serializable, and we need to keep the field name around when deserializing.
45         // Also, we'll make .NET FX 4.5 not break if it's missing.
46 #pragma warning disable 169
47         [OptionalField]
48         private char[] _tmpOneCharBuffer;
49 #pragma warning restore 169
50 
51         // Perf optimization stuff
52         private byte[] _largeByteBuffer;  // temp space for writing chars.
53         private int _maxChars;   // max # of chars we can put in _largeByteBuffer
54         // Size should be around the max number of chars/string * Encoding's max bytes/char
55         private const int LargeByteBufferSize = 256;
56 
57         // Protected default constructor that sets the output stream
58         // to a null stream (a bit bucket).
BinaryWriter()59         protected BinaryWriter()
60         {
61             OutStream = Stream.Null;
62             _buffer = new byte[16];
63             _encoding = new UTF8Encoding(false, true);
64             _encoder = _encoding.GetEncoder();
65         }
66 
BinaryWriter(Stream output)67         public BinaryWriter(Stream output) : this(output, new UTF8Encoding(false, true), false)
68         {
69         }
70 
BinaryWriter(Stream output, Encoding encoding)71         public BinaryWriter(Stream output, Encoding encoding) : this(output, encoding, false)
72         {
73         }
74 
BinaryWriter(Stream output, Encoding encoding, bool leaveOpen)75         public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen)
76         {
77             if (output==null)
78                 throw new ArgumentNullException("output");
79             if (encoding==null)
80                 throw new ArgumentNullException("encoding");
81             if (!output.CanWrite)
82                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable"));
83             Contract.EndContractBlock();
84 
85             OutStream = output;
86             _buffer = new byte[16];
87             _encoding = encoding;
88             _encoder = _encoding.GetEncoder();
89             _leaveOpen = leaveOpen;
90         }
91 
92         // Closes this writer and releases any system resources associated with the
93         // writer. Following a call to Close, any operations on the writer
94         // may raise exceptions.
Close()95         public virtual void Close()
96         {
97             Dispose(true);
98         }
99 
Dispose(bool disposing)100         protected virtual void Dispose(bool disposing)
101         {
102             if (disposing) {
103                 if (_leaveOpen)
104                     OutStream.Flush();
105                 else
106                     OutStream.Close();
107             }
108         }
109 
Dispose()110         public void Dispose()
111         {
112             Dispose(true);
113         }
114 
115         /*
116          * Returns the stream associate with the writer. It flushes all pending
117          * writes before returning. All subclasses should override Flush to
118          * ensure that all buffered data is sent to the stream.
119          */
120         public virtual Stream BaseStream {
121             get {
122                 Flush();
123                 return OutStream;
124             }
125         }
126 
127         // Clears all buffers for this writer and causes any buffered data to be
128         // written to the underlying device.
Flush()129         public virtual void Flush()
130         {
131             OutStream.Flush();
132         }
133 
Seek(int offset, SeekOrigin origin)134         public virtual long Seek(int offset, SeekOrigin origin)
135         {
136             return OutStream.Seek(offset, origin);
137         }
138 
139         // Writes a boolean to this stream. A single byte is written to the stream
140         // with the value 0 representing false or the value 1 representing true.
141         //
Write(bool value)142         public virtual void Write(bool value) {
143             _buffer[0] = (byte) (value ? 1 : 0);
144             OutStream.Write(_buffer, 0, 1);
145         }
146 
147         // Writes a byte to this stream. The current position of the stream is
148         // advanced by one.
149         //
Write(byte value)150         public virtual void Write(byte value)
151         {
152             OutStream.WriteByte(value);
153         }
154 
155         // Writes a signed byte to this stream. The current position of the stream
156         // is advanced by one.
157         //
158         [CLSCompliant(false)]
Write(sbyte value)159         public virtual void Write(sbyte value)
160         {
161             OutStream.WriteByte((byte) value);
162         }
163 
164         // Writes a byte array to this stream.
165         //
166         // This default implementation calls the Write(Object, int, int)
167         // method to write the byte array.
168         //
Write(byte[] buffer)169         public virtual void Write(byte[] buffer) {
170             if (buffer == null)
171                 throw new ArgumentNullException("buffer");
172             Contract.EndContractBlock();
173             OutStream.Write(buffer, 0, buffer.Length);
174         }
175 
176         // Writes a section of a byte array to this stream.
177         //
178         // This default implementation calls the Write(Object, int, int)
179         // method to write the byte array.
180         //
Write(byte[] buffer, int index, int count)181         public virtual void Write(byte[] buffer, int index, int count) {
182             OutStream.Write(buffer, index, count);
183         }
184 
185 
186         // Writes a character to this stream. The current position of the stream is
187         // advanced by two.
188         // Note this method cannot handle surrogates properly in UTF-8.
189         //
190         [System.Security.SecuritySafeCritical]  // auto-generated
Write(char ch)191         public unsafe virtual void Write(char ch) {
192             if (Char.IsSurrogate(ch))
193                 throw new ArgumentException(Environment.GetResourceString("Arg_SurrogatesNotAllowedAsSingleChar"));
194             Contract.EndContractBlock();
195 
196             Contract.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)");
197             int numBytes = 0;
198             fixed(byte * pBytes = _buffer) {
199                 numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, true);
200             }
201             OutStream.Write(_buffer, 0, numBytes);
202         }
203 
204         // Writes a character array to this stream.
205         //
206         // This default implementation calls the Write(Object, int, int)
207         // method to write the character array.
208         //
Write(char[] chars)209         public virtual void Write(char[] chars)
210         {
211             if (chars == null)
212                 throw new ArgumentNullException("chars");
213             Contract.EndContractBlock();
214 
215             byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length);
216             OutStream.Write(bytes, 0, bytes.Length);
217         }
218 
219         // Writes a section of a character array to this stream.
220         //
221         // This default implementation calls the Write(Object, int, int)
222         // method to write the character array.
223         //
Write(char[] chars, int index, int count)224         public virtual void Write(char[] chars, int index, int count)
225         {
226             byte[] bytes = _encoding.GetBytes(chars, index, count);
227             OutStream.Write(bytes, 0, bytes.Length);
228         }
229 
230 
231         // Writes a double to this stream. The current position of the stream is
232         // advanced by eight.
233         //
234         [System.Security.SecuritySafeCritical]  // auto-generated
Write(double value)235         public unsafe virtual void Write(double value)
236         {
237 #if MONO
238             OutStream.Write (Mono.Security.BitConverterLE.GetBytes (value), 0, 8);
239 #else
240             ulong TmpValue = *(ulong *)&value;
241             _buffer[0] = (byte) TmpValue;
242             _buffer[1] = (byte) (TmpValue >> 8);
243             _buffer[2] = (byte) (TmpValue >> 16);
244             _buffer[3] = (byte) (TmpValue >> 24);
245             _buffer[4] = (byte) (TmpValue >> 32);
246             _buffer[5] = (byte) (TmpValue >> 40);
247             _buffer[6] = (byte) (TmpValue >> 48);
248             _buffer[7] = (byte) (TmpValue >> 56);
249             OutStream.Write(_buffer, 0, 8);
250 #endif
251         }
252 
Write(decimal value)253         public virtual void Write(decimal value)
254         {
255             Decimal.GetBytes(value,_buffer);
256             OutStream.Write(_buffer, 0, 16);
257         }
258 
259         // Writes a two-byte signed integer to this stream. The current position of
260         // the stream is advanced by two.
261         //
Write(short value)262         public virtual void Write(short value)
263         {
264             _buffer[0] = (byte) value;
265             _buffer[1] = (byte) (value >> 8);
266             OutStream.Write(_buffer, 0, 2);
267         }
268 
269         // Writes a two-byte unsigned integer to this stream. The current position
270         // of the stream is advanced by two.
271         //
272         [CLSCompliant(false)]
Write(ushort value)273         public virtual void Write(ushort value)
274         {
275             _buffer[0] = (byte) value;
276             _buffer[1] = (byte) (value >> 8);
277             OutStream.Write(_buffer, 0, 2);
278         }
279 
280         // Writes a four-byte signed integer to this stream. The current position
281         // of the stream is advanced by four.
282         //
Write(int value)283         public virtual void Write(int value)
284         {
285             _buffer[0] = (byte) value;
286             _buffer[1] = (byte) (value >> 8);
287             _buffer[2] = (byte) (value >> 16);
288             _buffer[3] = (byte) (value >> 24);
289             OutStream.Write(_buffer, 0, 4);
290         }
291 
292         // Writes a four-byte unsigned integer to this stream. The current position
293         // of the stream is advanced by four.
294         //
295         [CLSCompliant(false)]
Write(uint value)296         public virtual void Write(uint value)
297         {
298             _buffer[0] = (byte) value;
299             _buffer[1] = (byte) (value >> 8);
300             _buffer[2] = (byte) (value >> 16);
301             _buffer[3] = (byte) (value >> 24);
302             OutStream.Write(_buffer, 0, 4);
303         }
304 
305         // Writes an eight-byte signed integer to this stream. The current position
306         // of the stream is advanced by eight.
307         //
Write(long value)308         public virtual void Write(long value)
309         {
310             _buffer[0] = (byte) value;
311             _buffer[1] = (byte) (value >> 8);
312             _buffer[2] = (byte) (value >> 16);
313             _buffer[3] = (byte) (value >> 24);
314             _buffer[4] = (byte) (value >> 32);
315             _buffer[5] = (byte) (value >> 40);
316             _buffer[6] = (byte) (value >> 48);
317             _buffer[7] = (byte) (value >> 56);
318             OutStream.Write(_buffer, 0, 8);
319         }
320 
321         // Writes an eight-byte unsigned integer to this stream. The current
322         // position of the stream is advanced by eight.
323         //
324         [CLSCompliant(false)]
Write(ulong value)325         public virtual void Write(ulong value)
326         {
327             _buffer[0] = (byte) value;
328             _buffer[1] = (byte) (value >> 8);
329             _buffer[2] = (byte) (value >> 16);
330             _buffer[3] = (byte) (value >> 24);
331             _buffer[4] = (byte) (value >> 32);
332             _buffer[5] = (byte) (value >> 40);
333             _buffer[6] = (byte) (value >> 48);
334             _buffer[7] = (byte) (value >> 56);
335             OutStream.Write(_buffer, 0, 8);
336         }
337 
338         // Writes a float to this stream. The current position of the stream is
339         // advanced by four.
340         //
341         [System.Security.SecuritySafeCritical]  // auto-generated
Write(float value)342         public unsafe virtual void Write(float value)
343         {
344 #if MONO
345             OutStream.Write (Mono.Security.BitConverterLE.GetBytes (value), 0, 4);
346 #else
347             uint TmpValue = *(uint *)&value;
348             _buffer[0] = (byte) TmpValue;
349             _buffer[1] = (byte) (TmpValue >> 8);
350             _buffer[2] = (byte) (TmpValue >> 16);
351             _buffer[3] = (byte) (TmpValue >> 24);
352             OutStream.Write(_buffer, 0, 4);
353 #endif
354         }
355 
356 
357         // Writes a length-prefixed string to this stream in the BinaryWriter's
358         // current Encoding. This method first writes the length of the string as
359         // a four-byte unsigned integer, and then writes that many characters
360         // to the stream.
361         //
362         [System.Security.SecuritySafeCritical]  // auto-generated
Write(String value)363         public unsafe virtual void Write(String value)
364         {
365             if (value==null)
366                 throw new ArgumentNullException("value");
367             Contract.EndContractBlock();
368 
369             int len = _encoding.GetByteCount(value);
370             Write7BitEncodedInt(len);
371 
372             if (_largeByteBuffer == null) {
373                 _largeByteBuffer = new byte[LargeByteBufferSize];
374                 _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);
375             }
376 
377             if (len <= _largeByteBuffer.Length) {
378                 //Contract.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers!  encoding type: "+_encoding.GetType().Name);
379                 _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0);
380                 OutStream.Write(_largeByteBuffer, 0, len);
381             }
382             else {
383                 // Aggressively try to not allocate memory in this loop for
384                 // runtime performance reasons.  Use an Encoder to write out
385                 // the string correctly (handling surrogates crossing buffer
386                 // boundaries properly).
387                 int charStart = 0;
388                 int numLeft = value.Length;
389 #if _DEBUG
390                 int totalBytes = 0;
391 #endif
392                 while (numLeft > 0) {
393                     // Figure out how many chars to process this round.
394                     int charCount = (numLeft > _maxChars) ? _maxChars : numLeft;
395                     int byteLen;
396 
397                     checked {
398                         if (charStart < 0 || charCount < 0 || charStart + charCount > value.Length) {
399                             throw new ArgumentOutOfRangeException("charCount");
400                         }
401 
402                         fixed(char* pChars = value) {
403                             fixed(byte* pBytes = _largeByteBuffer) {
404                                 byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft);
405                             }
406                         }
407                     }
408 #if _DEBUG
409                     totalBytes += byteLen;
410                     Contract.Assert (totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!");
411 #endif
412                     OutStream.Write(_largeByteBuffer, 0, byteLen);
413                     charStart += charCount;
414                     numLeft -= charCount;
415                 }
416 #if _DEBUG
417                 Contract.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
418 #endif
419             }
420         }
421 
Write7BitEncodedInt(int value)422         protected void Write7BitEncodedInt(int value) {
423             // Write out an int 7 bits at a time.  The high bit of the byte,
424             // when on, tells reader to continue reading more bytes.
425             uint v = (uint) value;   // support negative numbers
426             while (v >= 0x80) {
427                 Write((byte) (v | 0x80));
428                 v >>= 7;
429             }
430             Write((byte)v);
431         }
432     }
433 }
434