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