3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
10 ** <OWNER>gpaperin</OWNER>
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.
17 ===========================================================*/
20 using System
.Runtime
.Serialization
;
22 using System
.Diagnostics
.Contracts
;
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.
30 [System
.Runtime
.InteropServices
.ComVisible(true)]
31 public class BinaryWriter
: IDisposable
33 public static readonly BinaryWriter Null
= new BinaryWriter();
35 protected Stream OutStream
;
36 private byte[] _buffer
; // temp space for writing primitives to.
37 private Encoding _encoding
;
38 private Encoder _encoder
;
40 [OptionalField
] // New in .NET FX 4.5. False is the right default value.
41 private bool _leaveOpen
;
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
48 private char[] _tmpOneCharBuffer
;
49 #pragma warning restore 169
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;
57 // Protected default constructor that sets the output stream
58 // to a null stream (a bit bucket).
59 protected BinaryWriter()
61 OutStream
= Stream
.Null
;
62 _buffer
= new byte[16];
63 _encoding
= new UTF8Encoding(false, true);
64 _encoder
= _encoding
.GetEncoder();
67 public BinaryWriter(Stream output
) : this(output
, new UTF8Encoding(false, true), false)
71 public BinaryWriter(Stream output
, Encoding encoding
) : this(output
, encoding
, false)
75 public BinaryWriter(Stream output
, Encoding encoding
, bool leaveOpen
)
78 throw new ArgumentNullException("output");
80 throw new ArgumentNullException("encoding");
82 throw new ArgumentException(Environment
.GetResourceString("Argument_StreamNotWritable"));
83 Contract
.EndContractBlock();
86 _buffer
= new byte[16];
88 _encoder
= _encoding
.GetEncoder();
89 _leaveOpen
= leaveOpen
;
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.
95 public virtual void Close()
100 protected virtual void Dispose(bool disposing
)
110 public void Dispose()
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.
120 public virtual Stream BaseStream
{
127 // Clears all buffers for this writer and causes any buffered data to be
128 // written to the underlying device.
129 public virtual void Flush()
134 public virtual long Seek(int offset
, SeekOrigin origin
)
136 return OutStream
.Seek(offset
, origin
);
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.
142 public virtual void Write(bool value) {
143 _buffer
[0] = (byte) (value ? 1 : 0);
144 OutStream
.Write(_buffer
, 0, 1);
147 // Writes a byte to this stream. The current position of the stream is
150 public virtual void Write(byte value)
152 OutStream
.WriteByte(value);
155 // Writes a signed byte to this stream. The current position of the stream
156 // is advanced by one.
158 [CLSCompliant(false)]
159 public virtual void Write(sbyte value)
161 OutStream
.WriteByte((byte) value);
164 // Writes a byte array to this stream.
166 // This default implementation calls the Write(Object, int, int)
167 // method to write the byte array.
169 public virtual void Write(byte[] buffer
) {
171 throw new ArgumentNullException("buffer");
172 Contract
.EndContractBlock();
173 OutStream
.Write(buffer
, 0, buffer
.Length
);
176 // Writes a section of a byte array to this stream.
178 // This default implementation calls the Write(Object, int, int)
179 // method to write the byte array.
181 public virtual void Write(byte[] buffer
, int index
, int count
) {
182 OutStream
.Write(buffer
, index
, count
);
186 // Writes a character to this stream. The current position of the stream is
188 // Note this method cannot handle surrogates properly in UTF-8.
190 [System
.Security
.SecuritySafeCritical
] // auto-generated
191 public unsafe virtual void Write(char ch
) {
192 if (Char
.IsSurrogate(ch
))
193 throw new ArgumentException(Environment
.GetResourceString("Arg_SurrogatesNotAllowedAsSingleChar"));
194 Contract
.EndContractBlock();
196 Contract
.Assert(_encoding
.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)");
198 fixed(byte * pBytes
= _buffer
) {
199 numBytes
= _encoder
.GetBytes(&ch
, 1, pBytes
, _buffer
.Length
, true);
201 OutStream
.Write(_buffer
, 0, numBytes
);
204 // Writes a character array to this stream.
206 // This default implementation calls the Write(Object, int, int)
207 // method to write the character array.
209 public virtual void Write(char[] chars
)
212 throw new ArgumentNullException("chars");
213 Contract
.EndContractBlock();
215 byte[] bytes
= _encoding
.GetBytes(chars
, 0, chars
.Length
);
216 OutStream
.Write(bytes
, 0, bytes
.Length
);
219 // Writes a section of a character array to this stream.
221 // This default implementation calls the Write(Object, int, int)
222 // method to write the character array.
224 public virtual void Write(char[] chars
, int index
, int count
)
226 byte[] bytes
= _encoding
.GetBytes(chars
, index
, count
);
227 OutStream
.Write(bytes
, 0, bytes
.Length
);
231 // Writes a double to this stream. The current position of the stream is
232 // advanced by eight.
234 [System
.Security
.SecuritySafeCritical
] // auto-generated
235 public unsafe virtual void Write(double value)
238 OutStream
.Write (Mono
.Security
.BitConverterLE
.GetBytes (value), 0, 8);
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);
253 public virtual void Write(decimal value)
255 Decimal
.GetBytes(value,_buffer
);
256 OutStream
.Write(_buffer
, 0, 16);
259 // Writes a two-byte signed integer to this stream. The current position of
260 // the stream is advanced by two.
262 public virtual void Write(short value)
264 _buffer
[0] = (byte) value;
265 _buffer
[1] = (byte) (value >> 8);
266 OutStream
.Write(_buffer
, 0, 2);
269 // Writes a two-byte unsigned integer to this stream. The current position
270 // of the stream is advanced by two.
272 [CLSCompliant(false)]
273 public virtual void Write(ushort value)
275 _buffer
[0] = (byte) value;
276 _buffer
[1] = (byte) (value >> 8);
277 OutStream
.Write(_buffer
, 0, 2);
280 // Writes a four-byte signed integer to this stream. The current position
281 // of the stream is advanced by four.
283 public virtual void Write(int value)
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);
292 // Writes a four-byte unsigned integer to this stream. The current position
293 // of the stream is advanced by four.
295 [CLSCompliant(false)]
296 public virtual void Write(uint value)
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);
305 // Writes an eight-byte signed integer to this stream. The current position
306 // of the stream is advanced by eight.
308 public virtual void Write(long value)
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);
321 // Writes an eight-byte unsigned integer to this stream. The current
322 // position of the stream is advanced by eight.
324 [CLSCompliant(false)]
325 public virtual void Write(ulong value)
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);
338 // Writes a float to this stream. The current position of the stream is
341 [System
.Security
.SecuritySafeCritical
] // auto-generated
342 public unsafe virtual void Write(float value)
345 OutStream
.Write (Mono
.Security
.BitConverterLE
.GetBytes (value), 0, 4);
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);
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
362 [System
.Security
.SecuritySafeCritical
] // auto-generated
363 public unsafe virtual void Write(String
value)
366 throw new ArgumentNullException("value");
367 Contract
.EndContractBlock();
369 int len
= _encoding
.GetByteCount(value);
370 Write7BitEncodedInt(len
);
372 if (_largeByteBuffer
== null) {
373 _largeByteBuffer
= new byte[LargeByteBufferSize
];
374 _maxChars
= _largeByteBuffer
.Length
/ _encoding
.GetMaxByteCount(1);
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
);
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).
388 int numLeft
= value.Length
;
392 while (numLeft
> 0) {
393 // Figure out how many chars to process this round.
394 int charCount
= (numLeft
> _maxChars
) ? _maxChars
: numLeft
;
398 if (charStart
< 0 || charCount
< 0 || charStart
+ charCount
> value.Length
) {
399 throw new ArgumentOutOfRangeException("charCount");
402 fixed(char* pChars
= value) {
403 fixed(byte* pBytes
= _largeByteBuffer
) {
404 byteLen
= _encoder
.GetBytes(pChars
+ charStart
, charCount
, pBytes
, _largeByteBuffer
.Length
, charCount
== numLeft
);
409 totalBytes
+= byteLen
;
410 Contract
.Assert (totalBytes
<= len
&& byteLen
<= _largeByteBuffer
.Length
, "BinaryWriter::Write(String) - More bytes encoded than expected!");
412 OutStream
.Write(_largeByteBuffer
, 0, byteLen
);
413 charStart
+= charCount
;
414 numLeft
-= charCount
;
417 Contract
.Assert(totalBytes
== len
, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
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
427 Write((byte) (v
| 0x80));