diff options
Diffstat (limited to 'src/mscorlib/src/System/IO/BinaryWriter.cs')
-rw-r--r-- | src/mscorlib/src/System/IO/BinaryWriter.cs | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/IO/BinaryWriter.cs b/src/mscorlib/src/System/IO/BinaryWriter.cs new file mode 100644 index 0000000000..c775cbc9ff --- /dev/null +++ b/src/mscorlib/src/System/IO/BinaryWriter.cs @@ -0,0 +1,427 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** +** Purpose: Provides a way to write primitives types in +** binary from a Stream, while also supporting writing Strings +** in a particular encoding. +** +** +===========================================================*/ +using System; +using System.Runtime; +using System.Runtime.Serialization; +using System.Text; +using System.Diagnostics.Contracts; + +namespace System.IO { + // This abstract base class represents a writer that can write + // primitives to an arbitrary stream. A subclass can override methods to + // give unique encodings. + // + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public class BinaryWriter : IDisposable + { + public static readonly BinaryWriter Null = new BinaryWriter(); + + protected Stream OutStream; + private byte[] _buffer; // temp space for writing primitives to. + private Encoding _encoding; + private Encoder _encoder; + + [OptionalField] // New in .NET FX 4.5. False is the right default value. + private bool _leaveOpen; + + // This field should never have been serialized and has not been used since before v2.0. + // However, this type is serializable, and we need to keep the field name around when deserializing. + // Also, we'll make .NET FX 4.5 not break if it's missing. +#pragma warning disable 169 + [OptionalField] + private char[] _tmpOneCharBuffer; +#pragma warning restore 169 + + // Perf optimization stuff + private byte[] _largeByteBuffer; // temp space for writing chars. + private int _maxChars; // max # of chars we can put in _largeByteBuffer + // Size should be around the max number of chars/string * Encoding's max bytes/char + private const int LargeByteBufferSize = 256; + + // Protected default constructor that sets the output stream + // to a null stream (a bit bucket). + protected BinaryWriter() + { + OutStream = Stream.Null; + _buffer = new byte[16]; + _encoding = new UTF8Encoding(false, true); + _encoder = _encoding.GetEncoder(); + } + + public BinaryWriter(Stream output) : this(output, new UTF8Encoding(false, true), false) + { + } + + public BinaryWriter(Stream output, Encoding encoding) : this(output, encoding, false) + { + } + + public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen) + { + if (output==null) + throw new ArgumentNullException("output"); + if (encoding==null) + throw new ArgumentNullException("encoding"); + if (!output.CanWrite) + throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable")); + Contract.EndContractBlock(); + + OutStream = output; + _buffer = new byte[16]; + _encoding = encoding; + _encoder = _encoding.GetEncoder(); + _leaveOpen = leaveOpen; + } + + // Closes this writer and releases any system resources associated with the + // writer. Following a call to Close, any operations on the writer + // may raise exceptions. + public virtual void Close() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) { + if (_leaveOpen) + OutStream.Flush(); + else + OutStream.Close(); + } + } + + public void Dispose() + { + Dispose(true); + } + + /* + * Returns the stream associate with the writer. It flushes all pending + * writes before returning. All subclasses should override Flush to + * ensure that all buffered data is sent to the stream. + */ + public virtual Stream BaseStream { + get { + Flush(); + return OutStream; + } + } + + // Clears all buffers for this writer and causes any buffered data to be + // written to the underlying device. + public virtual void Flush() + { + OutStream.Flush(); + } + + public virtual long Seek(int offset, SeekOrigin origin) + { + return OutStream.Seek(offset, origin); + } + + // Writes a boolean to this stream. A single byte is written to the stream + // with the value 0 representing false or the value 1 representing true. + // + public virtual void Write(bool value) { + _buffer[0] = (byte) (value ? 1 : 0); + OutStream.Write(_buffer, 0, 1); + } + + // Writes a byte to this stream. The current position of the stream is + // advanced by one. + // + public virtual void Write(byte value) + { + OutStream.WriteByte(value); + } + + // Writes a signed byte to this stream. The current position of the stream + // is advanced by one. + // + [CLSCompliant(false)] + public virtual void Write(sbyte value) + { + OutStream.WriteByte((byte) value); + } + + // Writes a byte array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the byte array. + // + public virtual void Write(byte[] buffer) { + if (buffer == null) + throw new ArgumentNullException("buffer"); + Contract.EndContractBlock(); + OutStream.Write(buffer, 0, buffer.Length); + } + + // Writes a section of a byte array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the byte array. + // + public virtual void Write(byte[] buffer, int index, int count) { + OutStream.Write(buffer, index, count); + } + + + // Writes a character to this stream. The current position of the stream is + // advanced by two. + // Note this method cannot handle surrogates properly in UTF-8. + // + [System.Security.SecuritySafeCritical] // auto-generated + public unsafe virtual void Write(char ch) { + if (Char.IsSurrogate(ch)) + throw new ArgumentException(Environment.GetResourceString("Arg_SurrogatesNotAllowedAsSingleChar")); + Contract.EndContractBlock(); + + Contract.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); + int numBytes = 0; + fixed(byte * pBytes = _buffer) { + numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true); + } + OutStream.Write(_buffer, 0, numBytes); + } + + // Writes a character array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the character array. + // + public virtual void Write(char[] chars) + { + if (chars == null) + throw new ArgumentNullException("chars"); + Contract.EndContractBlock(); + + byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); + OutStream.Write(bytes, 0, bytes.Length); + } + + // Writes a section of a character array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the character array. + // + public virtual void Write(char[] chars, int index, int count) + { + byte[] bytes = _encoding.GetBytes(chars, index, count); + OutStream.Write(bytes, 0, bytes.Length); + } + + + // Writes a double to this stream. The current position of the stream is + // advanced by eight. + // + [System.Security.SecuritySafeCritical] // auto-generated + public unsafe virtual void Write(double value) + { + ulong TmpValue = *(ulong *)&value; + _buffer[0] = (byte) TmpValue; + _buffer[1] = (byte) (TmpValue >> 8); + _buffer[2] = (byte) (TmpValue >> 16); + _buffer[3] = (byte) (TmpValue >> 24); + _buffer[4] = (byte) (TmpValue >> 32); + _buffer[5] = (byte) (TmpValue >> 40); + _buffer[6] = (byte) (TmpValue >> 48); + _buffer[7] = (byte) (TmpValue >> 56); + OutStream.Write(_buffer, 0, 8); + } + + public virtual void Write(decimal value) + { + Decimal.GetBytes(value,_buffer); + OutStream.Write(_buffer, 0, 16); + } + + // Writes a two-byte signed integer to this stream. The current position of + // the stream is advanced by two. + // + public virtual void Write(short value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + OutStream.Write(_buffer, 0, 2); + } + + // Writes a two-byte unsigned integer to this stream. The current position + // of the stream is advanced by two. + // + [CLSCompliant(false)] + public virtual void Write(ushort value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + OutStream.Write(_buffer, 0, 2); + } + + // Writes a four-byte signed integer to this stream. The current position + // of the stream is advanced by four. + // + public virtual void Write(int value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + _buffer[2] = (byte) (value >> 16); + _buffer[3] = (byte) (value >> 24); + OutStream.Write(_buffer, 0, 4); + } + + // Writes a four-byte unsigned integer to this stream. The current position + // of the stream is advanced by four. + // + [CLSCompliant(false)] + public virtual void Write(uint value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + _buffer[2] = (byte) (value >> 16); + _buffer[3] = (byte) (value >> 24); + OutStream.Write(_buffer, 0, 4); + } + + // Writes an eight-byte signed integer to this stream. The current position + // of the stream is advanced by eight. + // + public virtual void Write(long value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + _buffer[2] = (byte) (value >> 16); + _buffer[3] = (byte) (value >> 24); + _buffer[4] = (byte) (value >> 32); + _buffer[5] = (byte) (value >> 40); + _buffer[6] = (byte) (value >> 48); + _buffer[7] = (byte) (value >> 56); + OutStream.Write(_buffer, 0, 8); + } + + // Writes an eight-byte unsigned integer to this stream. The current + // position of the stream is advanced by eight. + // + [CLSCompliant(false)] + public virtual void Write(ulong value) + { + _buffer[0] = (byte) value; + _buffer[1] = (byte) (value >> 8); + _buffer[2] = (byte) (value >> 16); + _buffer[3] = (byte) (value >> 24); + _buffer[4] = (byte) (value >> 32); + _buffer[5] = (byte) (value >> 40); + _buffer[6] = (byte) (value >> 48); + _buffer[7] = (byte) (value >> 56); + OutStream.Write(_buffer, 0, 8); + } + + // Writes a float to this stream. The current position of the stream is + // advanced by four. + // + [System.Security.SecuritySafeCritical] // auto-generated + public unsafe virtual void Write(float value) + { + uint TmpValue = *(uint *)&value; + _buffer[0] = (byte) TmpValue; + _buffer[1] = (byte) (TmpValue >> 8); + _buffer[2] = (byte) (TmpValue >> 16); + _buffer[3] = (byte) (TmpValue >> 24); + OutStream.Write(_buffer, 0, 4); + } + + + // Writes a length-prefixed string to this stream in the BinaryWriter's + // current Encoding. This method first writes the length of the string as + // a four-byte unsigned integer, and then writes that many characters + // to the stream. + // + [System.Security.SecuritySafeCritical] // auto-generated + public unsafe virtual void Write(String value) + { + if (value==null) + throw new ArgumentNullException("value"); + Contract.EndContractBlock(); + + int len = _encoding.GetByteCount(value); + Write7BitEncodedInt(len); + + if (_largeByteBuffer == null) { + _largeByteBuffer = new byte[LargeByteBufferSize]; + _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1); + } + + if (len <= _largeByteBuffer.Length) + { + //Contract.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); + _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); + OutStream.Write(_largeByteBuffer, 0, len); + } + else { + // Aggressively try to not allocate memory in this loop for + // runtime performance reasons. Use an Encoder to write out + // the string correctly (handling surrogates crossing buffer + // boundaries properly). + int charStart = 0; + int numLeft = value.Length; +#if _DEBUG + int totalBytes = 0; +#endif + while (numLeft > 0) { + // Figure out how many chars to process this round. + int charCount = (numLeft > _maxChars) ? _maxChars : numLeft; + int byteLen; + + checked + { + if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount) + { + throw new ArgumentOutOfRangeException(nameof(charCount)); + } + fixed (char* pChars = value) + { + fixed (byte* pBytes = _largeByteBuffer) + { + byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft); + } + } + } +#if _DEBUG + totalBytes += byteLen; + Contract.Assert (totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); +#endif + OutStream.Write(_largeByteBuffer, 0, byteLen); + charStart += charCount; + numLeft -= charCount; + } +#if _DEBUG + Contract.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!"); +#endif + } + } + + protected void Write7BitEncodedInt(int value) { + // Write out an int 7 bits at a time. The high bit of the byte, + // when on, tells reader to continue reading more bytes. + uint v = (uint) value; // support negative numbers + while (v >= 0x80) { + Write((byte) (v | 0x80)); + v >>= 7; + } + Write((byte)v); + } + } +} |