Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / io / binarywriter.cs
blobc6f81955e43a1a1b6225262f35862d9f61e372e1
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: BinaryWriter
9 **
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 ===========================================================*/
18 using System;
19 using System.Runtime;
20 using System.Runtime.Serialization;
21 using System.Text;
22 using System.Diagnostics.Contracts;
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.
29 [Serializable]
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
47 [OptionalField]
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)
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();
85 OutStream = output;
86 _buffer = new byte[16];
87 _encoding = encoding;
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()
97 Dispose(true);
100 protected virtual void Dispose(bool disposing)
102 if (disposing) {
103 if (_leaveOpen)
104 OutStream.Flush();
105 else
106 OutStream.Close();
110 public void Dispose()
112 Dispose(true);
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 {
121 get {
122 Flush();
123 return OutStream;
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()
131 OutStream.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
148 // advanced by one.
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) {
170 if (buffer == null)
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
187 // advanced by two.
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)");
197 int numBytes = 0;
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)
211 if (chars == null)
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)
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
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
339 // advanced by four.
341 [System.Security.SecuritySafeCritical] // auto-generated
342 public unsafe virtual void Write(float value)
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
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.
362 [System.Security.SecuritySafeCritical] // auto-generated
363 public unsafe virtual void Write(String value)
365 if (value==null)
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);
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;
397 checked {
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);
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;
416 #if _DEBUG
417 Contract.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
418 #endif
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;
430 Write((byte)v);