Fix StyleCop warning SA1206 (modifer ordering)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / IO / BinaryWriter.cs
blob1f20a5450f14a54aec1cd668eca62ae13ee64798
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Text;
6 using System.Diagnostics;
7 using System.Buffers;
8 using System.Threading.Tasks;
10 namespace System.IO
12 // This abstract base class represents a writer that can write
13 // primitives to an arbitrary stream. A subclass can override methods to
14 // give unique encodings.
16 public class BinaryWriter : IDisposable, IAsyncDisposable
18 public static readonly BinaryWriter Null = new BinaryWriter();
20 protected Stream OutStream;
21 private readonly byte[] _buffer; // temp space for writing primitives to.
22 private readonly Encoding _encoding;
23 private readonly Encoder _encoder;
25 private bool _leaveOpen;
27 // Perf optimization stuff
28 private byte[]? _largeByteBuffer; // temp space for writing chars.
29 private int _maxChars; // max # of chars we can put in _largeByteBuffer
30 // Size should be around the max number of chars/string * Encoding's max bytes/char
31 private const int LargeByteBufferSize = 256;
33 // Protected default constructor that sets the output stream
34 // to a null stream (a bit bucket).
35 protected BinaryWriter()
37 OutStream = Stream.Null;
38 _buffer = new byte[16];
39 _encoding = EncodingCache.UTF8NoBOM;
40 _encoder = _encoding.GetEncoder();
43 public BinaryWriter(Stream output) : this(output, EncodingCache.UTF8NoBOM, false)
47 public BinaryWriter(Stream output, Encoding encoding) : this(output, encoding, false)
51 public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen)
53 if (output == null)
54 throw new ArgumentNullException(nameof(output));
55 if (encoding == null)
56 throw new ArgumentNullException(nameof(encoding));
57 if (!output.CanWrite)
58 throw new ArgumentException(SR.Argument_StreamNotWritable);
60 OutStream = output;
61 _buffer = new byte[16];
62 _encoding = encoding;
63 _encoder = _encoding.GetEncoder();
64 _leaveOpen = leaveOpen;
67 // Closes this writer and releases any system resources associated with the
68 // writer. Following a call to Close, any operations on the writer
69 // may raise exceptions.
70 public virtual void Close()
72 Dispose(true);
75 protected virtual void Dispose(bool disposing)
77 if (disposing)
79 if (_leaveOpen)
80 OutStream.Flush();
81 else
82 OutStream.Close();
86 public void Dispose()
88 Dispose(true);
91 public virtual ValueTask DisposeAsync()
93 try
95 if (GetType() == typeof(BinaryWriter))
97 if (_leaveOpen)
99 return new ValueTask(OutStream.FlushAsync());
102 OutStream.Close();
104 else
106 // Since this is a derived BinaryWriter, delegate to whatever logic
107 // the derived implementation already has in Dispose.
108 Dispose();
111 return default;
113 catch (Exception exc)
115 return new ValueTask(Task.FromException(exc));
119 // Returns the stream associated with the writer. It flushes all pending
120 // writes before returning. All subclasses should override Flush to
121 // ensure that all buffered data is sent to the stream.
122 public virtual Stream BaseStream
126 Flush();
127 return OutStream;
131 // Clears all buffers for this writer and causes any buffered data to be
132 // written to the underlying device.
133 public virtual void Flush()
135 OutStream.Flush();
138 public virtual long Seek(int offset, SeekOrigin origin)
140 return OutStream.Seek(offset, origin);
143 // Writes a boolean to this stream. A single byte is written to the stream
144 // with the value 0 representing false or the value 1 representing true.
146 public virtual void Write(bool value)
148 _buffer[0] = (byte)(value ? 1 : 0);
149 OutStream.Write(_buffer, 0, 1);
152 // Writes a byte to this stream. The current position of the stream is
153 // advanced by one.
155 public virtual void Write(byte value)
157 OutStream.WriteByte(value);
160 // Writes a signed byte to this stream. The current position of the stream
161 // is advanced by one.
163 [CLSCompliant(false)]
164 public virtual void Write(sbyte value)
166 OutStream.WriteByte((byte)value);
169 // Writes a byte array to this stream.
171 // This default implementation calls the Write(Object, int, int)
172 // method to write the byte array.
174 public virtual void Write(byte[] buffer)
176 if (buffer == null)
177 throw new ArgumentNullException(nameof(buffer));
178 OutStream.Write(buffer, 0, buffer.Length);
181 // Writes a section of a byte array to this stream.
183 // This default implementation calls the Write(Object, int, int)
184 // method to write the byte array.
186 public virtual void Write(byte[] buffer, int index, int count)
188 OutStream.Write(buffer, index, count);
192 // Writes a character to this stream. The current position of the stream is
193 // advanced by two.
194 // Note this method cannot handle surrogates properly in UTF-8.
196 public virtual unsafe void Write(char ch)
198 if (char.IsSurrogate(ch))
199 throw new ArgumentException(SR.Arg_SurrogatesNotAllowedAsSingleChar);
201 Debug.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)");
202 int numBytes = 0;
203 fixed (byte* pBytes = &_buffer[0])
205 numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true);
207 OutStream.Write(_buffer, 0, numBytes);
210 // Writes a character array to this stream.
212 // This default implementation calls the Write(Object, int, int)
213 // method to write the character array.
215 public virtual void Write(char[] chars)
217 if (chars == null)
218 throw new ArgumentNullException(nameof(chars));
220 byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length);
221 OutStream.Write(bytes, 0, bytes.Length);
224 // Writes a section of a character array to this stream.
226 // This default implementation calls the Write(Object, int, int)
227 // method to write the character array.
229 public virtual void Write(char[] chars, int index, int count)
231 byte[] bytes = _encoding.GetBytes(chars, index, count);
232 OutStream.Write(bytes, 0, bytes.Length);
236 // Writes a double to this stream. The current position of the stream is
237 // advanced by eight.
239 public virtual unsafe void Write(double value)
241 ulong TmpValue = *(ulong*)&value;
242 _buffer[0] = (byte)TmpValue;
243 _buffer[1] = (byte)(TmpValue >> 8);
244 _buffer[2] = (byte)(TmpValue >> 16);
245 _buffer[3] = (byte)(TmpValue >> 24);
246 _buffer[4] = (byte)(TmpValue >> 32);
247 _buffer[5] = (byte)(TmpValue >> 40);
248 _buffer[6] = (byte)(TmpValue >> 48);
249 _buffer[7] = (byte)(TmpValue >> 56);
250 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
339 // advanced by four.
341 public virtual unsafe void Write(float value)
343 uint TmpValue = *(uint*)&value;
344 _buffer[0] = (byte)TmpValue;
345 _buffer[1] = (byte)(TmpValue >> 8);
346 _buffer[2] = (byte)(TmpValue >> 16);
347 _buffer[3] = (byte)(TmpValue >> 24);
348 OutStream.Write(_buffer, 0, 4);
352 // Writes a length-prefixed string to this stream in the BinaryWriter's
353 // current Encoding. This method first writes the length of the string as
354 // a four-byte unsigned integer, and then writes that many characters
355 // to the stream.
357 public virtual unsafe void Write(string value)
359 if (value == null)
360 throw new ArgumentNullException(nameof(value));
362 int len = _encoding.GetByteCount(value);
363 Write7BitEncodedInt(len);
365 if (_largeByteBuffer == null)
367 _largeByteBuffer = new byte[LargeByteBufferSize];
368 _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);
371 if (len <= _largeByteBuffer.Length)
373 //Debug.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name);
374 _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0);
375 OutStream.Write(_largeByteBuffer, 0, len);
377 else
379 // Aggressively try to not allocate memory in this loop for
380 // runtime performance reasons. Use an Encoder to write out
381 // the string correctly (handling surrogates crossing buffer
382 // boundaries properly).
383 int charStart = 0;
384 int numLeft = value.Length;
385 #if DEBUG
386 int totalBytes = 0;
387 #endif
388 while (numLeft > 0)
390 // Figure out how many chars to process this round.
391 int charCount = (numLeft > _maxChars) ? _maxChars : numLeft;
392 int byteLen;
394 checked
396 if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount)
398 throw new ArgumentOutOfRangeException(nameof(charCount));
400 fixed (char* pChars = value)
402 fixed (byte* pBytes = &_largeByteBuffer[0])
404 byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft);
408 #if DEBUG
409 totalBytes += byteLen;
410 Debug.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 Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!");
418 #endif
422 public virtual void Write(ReadOnlySpan<byte> buffer)
424 if (GetType() == typeof(BinaryWriter))
426 OutStream.Write(buffer);
428 else
430 byte[] array = ArrayPool<byte>.Shared.Rent(buffer.Length);
433 buffer.CopyTo(array);
434 Write(array, 0, buffer.Length);
436 finally
438 ArrayPool<byte>.Shared.Return(array);
443 public virtual void Write(ReadOnlySpan<char> chars)
445 byte[] bytes = ArrayPool<byte>.Shared.Rent(_encoding.GetMaxByteCount(chars.Length));
448 int bytesWritten = _encoding.GetBytes(chars, bytes);
449 Write(bytes, 0, bytesWritten);
451 finally
453 ArrayPool<byte>.Shared.Return(bytes);
457 protected void Write7BitEncodedInt(int value)
459 // Write out an int 7 bits at a time. The high bit of the byte,
460 // when on, tells reader to continue reading more bytes.
461 uint v = (uint)value; // support negative numbers
462 while (v >= 0x80)
464 Write((byte)(v | 0x80));
465 v >>= 7;
467 Write((byte)v);