BinaryReader.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  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.
  4. /*============================================================
  5. **
  6. **
  7. **
  8. **
  9. **
  10. ** Purpose: Wraps a stream and provides convenient read functionality
  11. ** for strings and primitive types.
  12. **
  13. **
  14. ============================================================*/
  15. using System.Diagnostics;
  16. using System.Runtime.InteropServices;
  17. using System.Text;
  18. namespace System.IO
  19. {
  20. public class BinaryReader : IDisposable
  21. {
  22. private const int MaxCharBytesSize = 128;
  23. private Stream _stream;
  24. private byte[] _buffer;
  25. private Decoder _decoder;
  26. private byte[] _charBytes;
  27. private char[] _singleChar;
  28. private char[] _charBuffer;
  29. private int _maxCharsSize; // From MaxCharBytesSize & Encoding
  30. // Performance optimization for Read() w/ Unicode. Speeds us up by ~40%
  31. private bool _2BytesPerChar;
  32. private bool _isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf
  33. private bool _leaveOpen;
  34. public BinaryReader(Stream input) : this(input, Encoding.UTF8, false)
  35. {
  36. }
  37. public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false)
  38. {
  39. }
  40. public BinaryReader(Stream input, Encoding encoding, bool leaveOpen)
  41. {
  42. if (input == null)
  43. {
  44. throw new ArgumentNullException(nameof(input));
  45. }
  46. if (encoding == null)
  47. {
  48. throw new ArgumentNullException(nameof(encoding));
  49. }
  50. if (!input.CanRead)
  51. {
  52. throw new ArgumentException(SR.Argument_StreamNotReadable);
  53. }
  54. _stream = input;
  55. _decoder = encoding.GetDecoder();
  56. _maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize);
  57. int minBufferSize = encoding.GetMaxByteCount(1); // max bytes per one char
  58. if (minBufferSize < 16)
  59. {
  60. minBufferSize = 16;
  61. }
  62. _buffer = new byte[minBufferSize];
  63. // _charBuffer and _charBytes will be left null.
  64. // For Encodings that always use 2 bytes per char (or more),
  65. // special case them here to make Read() & Peek() faster.
  66. _2BytesPerChar = encoding is UnicodeEncoding;
  67. // check if BinaryReader is based on MemoryStream, and keep this for it's life
  68. // we cannot use "as" operator, since derived classes are not allowed
  69. _isMemoryStream = (_stream.GetType() == typeof(MemoryStream));
  70. _leaveOpen = leaveOpen;
  71. Debug.Assert(_decoder != null, "[BinaryReader.ctor]_decoder!=null");
  72. }
  73. public virtual Stream BaseStream
  74. {
  75. get
  76. {
  77. return _stream;
  78. }
  79. }
  80. protected virtual void Dispose(bool disposing)
  81. {
  82. if (disposing)
  83. {
  84. Stream copyOfStream = _stream;
  85. _stream = null;
  86. if (copyOfStream != null && !_leaveOpen)
  87. {
  88. copyOfStream.Close();
  89. }
  90. }
  91. _stream = null;
  92. _buffer = null;
  93. _decoder = null;
  94. _charBytes = null;
  95. _singleChar = null;
  96. _charBuffer = null;
  97. }
  98. public void Dispose()
  99. {
  100. Dispose(true);
  101. }
  102. /// <remarks>
  103. /// Override Dispose(bool) instead of Close(). This API exists for compatibility purposes.
  104. /// </remarks>
  105. public virtual void Close()
  106. {
  107. Dispose(true);
  108. }
  109. public virtual int PeekChar()
  110. {
  111. if (_stream == null)
  112. {
  113. throw Error.GetFileNotOpen();
  114. }
  115. if (!_stream.CanSeek)
  116. {
  117. return -1;
  118. }
  119. long origPos = _stream.Position;
  120. int ch = Read();
  121. _stream.Position = origPos;
  122. return ch;
  123. }
  124. public virtual int Read()
  125. {
  126. if (_stream == null)
  127. {
  128. throw Error.GetFileNotOpen();
  129. }
  130. int charsRead = 0;
  131. int numBytes = 0;
  132. long posSav = posSav = 0;
  133. if (_stream.CanSeek)
  134. {
  135. posSav = _stream.Position;
  136. }
  137. if (_charBytes == null)
  138. {
  139. _charBytes = new byte[MaxCharBytesSize]; //REVIEW: We need at most 2 bytes/char here?
  140. }
  141. if (_singleChar == null)
  142. {
  143. _singleChar = new char[1];
  144. }
  145. while (charsRead == 0)
  146. {
  147. // We really want to know what the minimum number of bytes per char
  148. // is for our encoding. Otherwise for UnicodeEncoding we'd have to
  149. // do ~1+log(n) reads to read n characters.
  150. // Assume 1 byte can be 1 char unless _2BytesPerChar is true.
  151. numBytes = _2BytesPerChar ? 2 : 1;
  152. int r = _stream.ReadByte();
  153. _charBytes[0] = (byte)r;
  154. if (r == -1)
  155. {
  156. numBytes = 0;
  157. }
  158. if (numBytes == 2)
  159. {
  160. r = _stream.ReadByte();
  161. _charBytes[1] = (byte)r;
  162. if (r == -1)
  163. {
  164. numBytes = 1;
  165. }
  166. }
  167. if (numBytes == 0)
  168. {
  169. return -1;
  170. }
  171. Debug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::ReadOneChar assumes it's reading one or 2 bytes only.");
  172. try
  173. {
  174. charsRead = _decoder.GetChars(_charBytes, 0, numBytes, _singleChar, 0);
  175. }
  176. catch
  177. {
  178. // Handle surrogate char
  179. if (_stream.CanSeek)
  180. {
  181. _stream.Seek((posSav - _stream.Position), SeekOrigin.Current);
  182. }
  183. // else - we can't do much here
  184. throw;
  185. }
  186. Debug.Assert(charsRead < 2, "BinaryReader::ReadOneChar - assuming we only got 0 or 1 char, not 2!");
  187. }
  188. Debug.Assert(charsRead > 0);
  189. return _singleChar[0];
  190. }
  191. public virtual bool ReadBoolean()
  192. {
  193. FillBuffer(1);
  194. return (_buffer[0] != 0);
  195. }
  196. public virtual byte ReadByte()
  197. {
  198. // Inlined to avoid some method call overhead with FillBuffer.
  199. if (_stream == null)
  200. {
  201. throw Error.GetFileNotOpen();
  202. }
  203. int b = _stream.ReadByte();
  204. if (b == -1)
  205. {
  206. throw Error.GetEndOfFile();
  207. }
  208. return (byte)b;
  209. }
  210. [CLSCompliant(false)]
  211. public virtual sbyte ReadSByte()
  212. {
  213. FillBuffer(1);
  214. return (sbyte)(_buffer[0]);
  215. }
  216. public virtual char ReadChar()
  217. {
  218. int value = Read();
  219. if (value == -1)
  220. {
  221. throw Error.GetEndOfFile();
  222. }
  223. return (char)value;
  224. }
  225. public virtual short ReadInt16()
  226. {
  227. FillBuffer(2);
  228. return (short)(_buffer[0] | _buffer[1] << 8);
  229. }
  230. [CLSCompliant(false)]
  231. public virtual ushort ReadUInt16()
  232. {
  233. FillBuffer(2);
  234. return (ushort)(_buffer[0] | _buffer[1] << 8);
  235. }
  236. public virtual int ReadInt32()
  237. {
  238. if (_isMemoryStream)
  239. {
  240. if (_stream == null)
  241. {
  242. throw Error.GetFileNotOpen();
  243. }
  244. // read directly from MemoryStream buffer
  245. MemoryStream mStream = _stream as MemoryStream;
  246. Debug.Assert(mStream != null, "_stream as MemoryStream != null");
  247. return mStream.InternalReadInt32();
  248. }
  249. else
  250. {
  251. FillBuffer(4);
  252. return (int)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
  253. }
  254. }
  255. [CLSCompliant(false)]
  256. public virtual uint ReadUInt32()
  257. {
  258. FillBuffer(4);
  259. return (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
  260. }
  261. public virtual long ReadInt64()
  262. {
  263. FillBuffer(8);
  264. uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
  265. _buffer[2] << 16 | _buffer[3] << 24);
  266. uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
  267. _buffer[6] << 16 | _buffer[7] << 24);
  268. return (long)((ulong)hi) << 32 | lo;
  269. }
  270. [CLSCompliant(false)]
  271. public virtual ulong ReadUInt64()
  272. {
  273. FillBuffer(8);
  274. uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
  275. _buffer[2] << 16 | _buffer[3] << 24);
  276. uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
  277. _buffer[6] << 16 | _buffer[7] << 24);
  278. return ((ulong)hi) << 32 | lo;
  279. }
  280. public virtual unsafe float ReadSingle()
  281. {
  282. FillBuffer(4);
  283. uint tmpBuffer = (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
  284. return *((float*)&tmpBuffer);
  285. }
  286. public virtual unsafe double ReadDouble()
  287. {
  288. FillBuffer(8);
  289. uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
  290. _buffer[2] << 16 | _buffer[3] << 24);
  291. uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
  292. _buffer[6] << 16 | _buffer[7] << 24);
  293. ulong tmpBuffer = ((ulong)hi) << 32 | lo;
  294. return *((double*)&tmpBuffer);
  295. }
  296. public virtual decimal ReadDecimal()
  297. {
  298. FillBuffer(16);
  299. try
  300. {
  301. return decimal.ToDecimal(_buffer);
  302. }
  303. catch (ArgumentException e)
  304. {
  305. // ReadDecimal cannot leak out ArgumentException
  306. throw new IOException(SR.Arg_DecBitCtor, e);
  307. }
  308. }
  309. public virtual string ReadString()
  310. {
  311. if (_stream == null)
  312. {
  313. throw Error.GetFileNotOpen();
  314. }
  315. int currPos = 0;
  316. int n;
  317. int stringLength;
  318. int readLength;
  319. int charsRead;
  320. // Length of the string in bytes, not chars
  321. stringLength = Read7BitEncodedInt();
  322. if (stringLength < 0)
  323. {
  324. throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength));
  325. }
  326. if (stringLength == 0)
  327. {
  328. return string.Empty;
  329. }
  330. if (_charBytes == null)
  331. {
  332. _charBytes = new byte[MaxCharBytesSize];
  333. }
  334. if (_charBuffer == null)
  335. {
  336. _charBuffer = new char[_maxCharsSize];
  337. }
  338. StringBuilder sb = null;
  339. do
  340. {
  341. readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos);
  342. n = _stream.Read(_charBytes, 0, readLength);
  343. if (n == 0)
  344. {
  345. throw Error.GetEndOfFile();
  346. }
  347. charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0);
  348. if (currPos == 0 && n == stringLength)
  349. {
  350. return new string(_charBuffer, 0, charsRead);
  351. }
  352. if (sb == null)
  353. {
  354. sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller.
  355. }
  356. sb.Append(_charBuffer, 0, charsRead);
  357. currPos += n;
  358. } while (currPos < stringLength);
  359. return StringBuilderCache.GetStringAndRelease(sb);
  360. }
  361. public virtual int Read(char[] buffer, int index, int count)
  362. {
  363. if (buffer == null)
  364. {
  365. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  366. }
  367. if (index < 0)
  368. {
  369. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  370. }
  371. if (count < 0)
  372. {
  373. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  374. }
  375. if (buffer.Length - index < count)
  376. {
  377. throw new ArgumentException(SR.Argument_InvalidOffLen);
  378. }
  379. if (_stream == null)
  380. {
  381. throw Error.GetFileNotOpen();
  382. }
  383. // SafeCritical: index and count have already been verified to be a valid range for the buffer
  384. return InternalReadChars(new Span<char>(buffer, index, count));
  385. }
  386. public virtual int Read(Span<char> buffer)
  387. {
  388. if (_stream == null)
  389. {
  390. throw Error.GetFileNotOpen();
  391. }
  392. return InternalReadChars(buffer);
  393. }
  394. private int InternalReadChars(Span<char> buffer)
  395. {
  396. Debug.Assert(_stream != null);
  397. int numBytes = 0;
  398. int index = 0;
  399. int charsRemaining = buffer.Length;
  400. if (_charBytes == null)
  401. {
  402. _charBytes = new byte[MaxCharBytesSize];
  403. }
  404. while (charsRemaining > 0)
  405. {
  406. int charsRead = 0;
  407. // We really want to know what the minimum number of bytes per char
  408. // is for our encoding. Otherwise for UnicodeEncoding we'd have to
  409. // do ~1+log(n) reads to read n characters.
  410. numBytes = charsRemaining;
  411. if (_2BytesPerChar)
  412. {
  413. numBytes <<= 1;
  414. }
  415. if (numBytes > MaxCharBytesSize)
  416. {
  417. numBytes = MaxCharBytesSize;
  418. }
  419. int position = 0;
  420. byte[] byteBuffer = null;
  421. if (_isMemoryStream)
  422. {
  423. MemoryStream mStream = _stream as MemoryStream;
  424. Debug.Assert(mStream != null, "_stream as MemoryStream != null");
  425. position = mStream.InternalGetPosition();
  426. numBytes = mStream.InternalEmulateRead(numBytes);
  427. byteBuffer = mStream.InternalGetBuffer();
  428. }
  429. else
  430. {
  431. numBytes = _stream.Read(_charBytes, 0, numBytes);
  432. byteBuffer = _charBytes;
  433. }
  434. if (numBytes == 0)
  435. {
  436. return (buffer.Length - charsRemaining);
  437. }
  438. Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null");
  439. checked
  440. {
  441. if (position < 0 || numBytes < 0 || position > byteBuffer.Length - numBytes)
  442. {
  443. throw new ArgumentOutOfRangeException(nameof(numBytes));
  444. }
  445. if (index < 0 || charsRemaining < 0 || index > buffer.Length - charsRemaining)
  446. {
  447. throw new ArgumentOutOfRangeException(nameof(charsRemaining));
  448. }
  449. unsafe
  450. {
  451. fixed (byte* pBytes = byteBuffer)
  452. fixed (char* pChars = &MemoryMarshal.GetReference(buffer))
  453. {
  454. charsRead = _decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, flush: false);
  455. }
  456. }
  457. }
  458. charsRemaining -= charsRead;
  459. index += charsRead;
  460. }
  461. // this should never fail
  462. Debug.Assert(charsRemaining >= 0, "We read too many characters.");
  463. // we may have read fewer than the number of characters requested if end of stream reached
  464. // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence)
  465. return (buffer.Length - charsRemaining);
  466. }
  467. public virtual char[] ReadChars(int count)
  468. {
  469. if (count < 0)
  470. {
  471. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  472. }
  473. if (_stream == null)
  474. {
  475. throw Error.GetFileNotOpen();
  476. }
  477. if (count == 0)
  478. {
  479. return Array.Empty<char>();
  480. }
  481. // SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid
  482. char[] chars = new char[count];
  483. int n = InternalReadChars(new Span<char>(chars));
  484. if (n != count)
  485. {
  486. char[] copy = new char[n];
  487. Buffer.BlockCopy(chars, 0, copy, 0, 2 * n); // sizeof(char)
  488. chars = copy;
  489. }
  490. return chars;
  491. }
  492. public virtual int Read(byte[] buffer, int index, int count)
  493. {
  494. if (buffer == null)
  495. {
  496. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  497. }
  498. if (index < 0)
  499. {
  500. throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
  501. }
  502. if (count < 0)
  503. {
  504. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  505. }
  506. if (buffer.Length - index < count)
  507. {
  508. throw new ArgumentException(SR.Argument_InvalidOffLen);
  509. }
  510. if (_stream == null)
  511. {
  512. throw Error.GetFileNotOpen();
  513. }
  514. return _stream.Read(buffer, index, count);
  515. }
  516. public virtual int Read(Span<byte> buffer)
  517. {
  518. if (_stream == null)
  519. {
  520. throw Error.GetFileNotOpen();
  521. }
  522. return _stream.Read(buffer);
  523. }
  524. public virtual byte[] ReadBytes(int count)
  525. {
  526. if (count < 0)
  527. {
  528. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  529. }
  530. if (_stream == null)
  531. {
  532. throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed);
  533. }
  534. if (count == 0)
  535. {
  536. return Array.Empty<byte>();
  537. }
  538. byte[] result = new byte[count];
  539. int numRead = 0;
  540. do
  541. {
  542. int n = _stream.Read(result, numRead, count);
  543. if (n == 0)
  544. {
  545. break;
  546. }
  547. numRead += n;
  548. count -= n;
  549. } while (count > 0);
  550. if (numRead != result.Length)
  551. {
  552. // Trim array. This should happen on EOF & possibly net streams.
  553. byte[] copy = new byte[numRead];
  554. Buffer.BlockCopy(result, 0, copy, 0, numRead);
  555. result = copy;
  556. }
  557. return result;
  558. }
  559. protected virtual void FillBuffer(int numBytes)
  560. {
  561. if (_buffer != null && (numBytes < 0 || numBytes > _buffer.Length))
  562. {
  563. throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_BinaryReaderFillBuffer);
  564. }
  565. int bytesRead = 0;
  566. int n = 0;
  567. if (_stream == null)
  568. {
  569. throw Error.GetFileNotOpen();
  570. }
  571. // Need to find a good threshold for calling ReadByte() repeatedly
  572. // vs. calling Read(byte[], int, int) for both buffered & unbuffered
  573. // streams.
  574. if (numBytes == 1)
  575. {
  576. n = _stream.ReadByte();
  577. if (n == -1)
  578. {
  579. throw Error.GetEndOfFile();
  580. }
  581. _buffer[0] = (byte)n;
  582. return;
  583. }
  584. do
  585. {
  586. n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead);
  587. if (n == 0)
  588. {
  589. throw Error.GetEndOfFile();
  590. }
  591. bytesRead += n;
  592. } while (bytesRead < numBytes);
  593. }
  594. protected internal int Read7BitEncodedInt()
  595. {
  596. // Read out an Int32 7 bits at a time. The high bit
  597. // of the byte when on means to continue reading more bytes.
  598. int count = 0;
  599. int shift = 0;
  600. byte b;
  601. do
  602. {
  603. // Check for a corrupted stream. Read a max of 5 bytes.
  604. // In a future version, add a DataFormatException.
  605. if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7
  606. {
  607. throw new FormatException(SR.Format_Bad7BitInt32);
  608. }
  609. // ReadByte handles end of stream cases for us.
  610. b = ReadByte();
  611. count |= (b & 0x7F) << shift;
  612. shift += 7;
  613. } while ((b & 0x80) != 0);
  614. return count;
  615. }
  616. }
  617. }