erlang自定义二进制协议
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

598 líneas
16 KiB

  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using MiscUtil.Conversion;
  5. namespace MiscUtil.IO
  6. {
  7. /// <summary>
  8. /// Equivalent of System.IO.BinaryReader, but with either endianness, depending on
  9. /// the EndianBitConverter it is constructed with. No data is buffered in the
  10. /// reader; the client may seek within the stream at will.
  11. /// </summary>
  12. public class EndianBinaryReader : IDisposable
  13. {
  14. #region Fields not directly related to properties
  15. /// <summary>
  16. /// Whether or not this reader has been disposed yet.
  17. /// </summary>
  18. bool disposed=false;
  19. /// <summary>
  20. /// Decoder to use for string conversions.
  21. /// </summary>
  22. Decoder decoder;
  23. /// <summary>
  24. /// Buffer used for temporary storage before conversion into primitives
  25. /// </summary>
  26. byte[] buffer = new byte[16];
  27. /// <summary>
  28. /// Buffer used for temporary storage when reading a single character
  29. /// </summary>
  30. char[] charBuffer = new char[1];
  31. /// <summary>
  32. /// Minimum number of bytes used to encode a character
  33. /// </summary>
  34. int minBytesPerChar;
  35. #endregion
  36. #region Constructors
  37. /// <summary>
  38. /// Equivalent of System.IO.BinaryWriter, but with either endianness, depending on
  39. /// the EndianBitConverter it is constructed with.
  40. /// </summary>
  41. /// <param name="bitConverter">Converter to use when reading data</param>
  42. /// <param name="stream">Stream to read data from</param>
  43. public EndianBinaryReader (EndianBitConverter bitConverter,
  44. Stream stream) : this (bitConverter, stream, Encoding.UTF8)
  45. {
  46. }
  47. /// <summary>
  48. /// Constructs a new binary reader with the given bit converter, reading
  49. /// to the given stream, using the given encoding.
  50. /// </summary>
  51. /// <param name="bitConverter">Converter to use when reading data</param>
  52. /// <param name="stream">Stream to read data from</param>
  53. /// <param name="encoding">Encoding to use when reading character data</param>
  54. public EndianBinaryReader (EndianBitConverter bitConverter, Stream stream, Encoding encoding)
  55. {
  56. if (bitConverter==null)
  57. {
  58. throw new ArgumentNullException("bitConverter");
  59. }
  60. if (stream==null)
  61. {
  62. throw new ArgumentNullException("stream");
  63. }
  64. if (encoding==null)
  65. {
  66. throw new ArgumentNullException("encoding");
  67. }
  68. if (!stream.CanRead)
  69. {
  70. throw new ArgumentException("Stream isn't writable", "stream");
  71. }
  72. this.stream = stream;
  73. this.bitConverter = bitConverter;
  74. this.encoding = encoding;
  75. this.decoder = encoding.GetDecoder();
  76. this.minBytesPerChar = 1;
  77. if (encoding is UnicodeEncoding)
  78. {
  79. minBytesPerChar = 2;
  80. }
  81. }
  82. #endregion
  83. #region Properties
  84. EndianBitConverter bitConverter;
  85. /// <summary>
  86. /// The bit converter used to read values from the stream
  87. /// </summary>
  88. public EndianBitConverter BitConverter
  89. {
  90. get { return bitConverter; }
  91. }
  92. Encoding encoding;
  93. /// <summary>
  94. /// The encoding used to read strings
  95. /// </summary>
  96. public Encoding Encoding
  97. {
  98. get { return encoding; }
  99. }
  100. Stream stream;
  101. /// <summary>
  102. /// Gets the underlying stream of the EndianBinaryReader.
  103. /// </summary>
  104. public Stream BaseStream
  105. {
  106. get { return stream; }
  107. }
  108. #endregion
  109. #region Public methods
  110. /// <summary>
  111. /// Closes the reader, including the underlying stream..
  112. /// </summary>
  113. public void Close()
  114. {
  115. Dispose();
  116. }
  117. /// <summary>
  118. /// Seeks within the stream.
  119. /// </summary>
  120. /// <param name="offset">Offset to seek to.</param>
  121. /// <param name="origin">Origin of seek operation.</param>
  122. public void Seek (int offset, SeekOrigin origin)
  123. {
  124. CheckDisposed();
  125. stream.Seek (offset, origin);
  126. }
  127. /// <summary>
  128. /// Reads a single byte from the stream.
  129. /// </summary>
  130. /// <returns>The byte read</returns>
  131. public byte ReadByte()
  132. {
  133. ReadInternal(buffer, 1);
  134. return buffer[0];
  135. }
  136. /// <summary>
  137. /// Reads a single signed byte from the stream.
  138. /// </summary>
  139. /// <returns>The byte read</returns>
  140. public sbyte ReadSByte()
  141. {
  142. ReadInternal(buffer, 1);
  143. return unchecked((sbyte)buffer[0]);
  144. }
  145. /// <summary>
  146. /// Reads a boolean from the stream. 1 byte is read.
  147. /// </summary>
  148. /// <returns>The boolean read</returns>
  149. public bool ReadBoolean()
  150. {
  151. ReadInternal(buffer, 1);
  152. return bitConverter.ToBoolean(buffer, 0);
  153. }
  154. /// <summary>
  155. /// Reads a 16-bit signed integer from the stream, using the bit converter
  156. /// for this reader. 2 bytes are read.
  157. /// </summary>
  158. /// <returns>The 16-bit integer read</returns>
  159. public short ReadInt16()
  160. {
  161. ReadInternal(buffer, 2);
  162. return bitConverter.ToInt16(buffer, 0);
  163. }
  164. /// <summary>
  165. /// Reads a 32-bit signed integer from the stream, using the bit converter
  166. /// for this reader. 4 bytes are read.
  167. /// </summary>
  168. /// <returns>The 32-bit integer read</returns>
  169. public int ReadInt32()
  170. {
  171. ReadInternal(buffer, 4);
  172. return bitConverter.ToInt32(buffer, 0);
  173. }
  174. /// <summary>
  175. /// Reads a 64-bit signed integer from the stream, using the bit converter
  176. /// for this reader. 8 bytes are read.
  177. /// </summary>
  178. /// <returns>The 64-bit integer read</returns>
  179. public long ReadInt64()
  180. {
  181. ReadInternal(buffer, 8);
  182. return bitConverter.ToInt64(buffer, 0);
  183. }
  184. /// <summary>
  185. /// Reads a 16-bit unsigned integer from the stream, using the bit converter
  186. /// for this reader. 2 bytes are read.
  187. /// </summary>
  188. /// <returns>The 16-bit unsigned integer read</returns>
  189. public ushort ReadUInt16()
  190. {
  191. ReadInternal(buffer, 2);
  192. return bitConverter.ToUInt16(buffer, 0);
  193. }
  194. /// <summary>
  195. /// Reads a 32-bit unsigned integer from the stream, using the bit converter
  196. /// for this reader. 4 bytes are read.
  197. /// </summary>
  198. /// <returns>The 32-bit unsigned integer read</returns>
  199. public uint ReadUInt32()
  200. {
  201. ReadInternal(buffer, 4);
  202. return bitConverter.ToUInt32(buffer, 0);
  203. }
  204. /// <summary>
  205. /// Reads a 64-bit unsigned integer from the stream, using the bit converter
  206. /// for this reader. 8 bytes are read.
  207. /// </summary>
  208. /// <returns>The 64-bit unsigned integer read</returns>
  209. public ulong ReadUInt64()
  210. {
  211. ReadInternal(buffer, 8);
  212. return bitConverter.ToUInt64(buffer, 0);
  213. }
  214. /// <summary>
  215. /// Reads a single-precision floating-point value from the stream, using the bit converter
  216. /// for this reader. 4 bytes are read.
  217. /// </summary>
  218. /// <returns>The floating point value read</returns>
  219. public float ReadSingle()
  220. {
  221. ReadInternal(buffer, 4);
  222. return bitConverter.ToSingle(buffer, 0);
  223. }
  224. /// <summary>
  225. /// Reads a double-precision floating-point value from the stream, using the bit converter
  226. /// for this reader. 8 bytes are read.
  227. /// </summary>
  228. /// <returns>The floating point value read</returns>
  229. public double ReadDouble()
  230. {
  231. ReadInternal(buffer, 8);
  232. return bitConverter.ToDouble(buffer, 0);
  233. }
  234. /// <summary>
  235. /// Reads a decimal value from the stream, using the bit converter
  236. /// for this reader. 16 bytes are read.
  237. /// </summary>
  238. /// <returns>The decimal value read</returns>
  239. public decimal ReadDecimal()
  240. {
  241. ReadInternal(buffer, 16);
  242. return bitConverter.ToDecimal(buffer, 0);
  243. }
  244. /// <summary>
  245. /// Reads a single character from the stream, using the character encoding for
  246. /// this reader. If no characters have been fully read by the time the stream ends,
  247. /// -1 is returned.
  248. /// </summary>
  249. /// <returns>The character read, or -1 for end of stream.</returns>
  250. public int Read()
  251. {
  252. int charsRead = Read(charBuffer, 0, 1);
  253. if (charsRead==0)
  254. {
  255. return -1;
  256. }
  257. else
  258. {
  259. return charBuffer[0];
  260. }
  261. }
  262. /// <summary>
  263. /// Reads the specified number of characters into the given buffer, starting at
  264. /// the given index.
  265. /// </summary>
  266. /// <param name="data">The buffer to copy data into</param>
  267. /// <param name="index">The first index to copy data into</param>
  268. /// <param name="count">The number of characters to read</param>
  269. /// <returns>The number of characters actually read. This will only be less than
  270. /// the requested number of characters if the end of the stream is reached.
  271. /// </returns>
  272. public int Read(char[] data, int index, int count)
  273. {
  274. CheckDisposed();
  275. if (buffer==null)
  276. {
  277. throw new ArgumentNullException("buffer");
  278. }
  279. if (index < 0)
  280. {
  281. throw new ArgumentOutOfRangeException("index");
  282. }
  283. if (count < 0)
  284. {
  285. throw new ArgumentOutOfRangeException("index");
  286. }
  287. if (count+index > data.Length)
  288. {
  289. throw new ArgumentException
  290. ("Not enough space in buffer for specified number of characters starting at specified index");
  291. }
  292. int read=0;
  293. bool firstTime=true;
  294. // Use the normal buffer if we're only reading a small amount, otherwise
  295. // use at most 4K at a time.
  296. byte[] byteBuffer = buffer;
  297. if (byteBuffer.Length < count*minBytesPerChar)
  298. {
  299. byteBuffer = new byte[4096];
  300. }
  301. while (read < count)
  302. {
  303. int amountToRead;
  304. // First time through we know we haven't previously read any data
  305. if (firstTime)
  306. {
  307. amountToRead = count*minBytesPerChar;
  308. firstTime=false;
  309. }
  310. // After that we can only assume we need to fully read "chars left -1" characters
  311. // and a single byte of the character we may be in the middle of
  312. else
  313. {
  314. amountToRead = ((count-read-1)*minBytesPerChar)+1;
  315. }
  316. if (amountToRead > byteBuffer.Length)
  317. {
  318. amountToRead = byteBuffer.Length;
  319. }
  320. int bytesRead = TryReadInternal(byteBuffer, amountToRead);
  321. if (bytesRead==0)
  322. {
  323. return read;
  324. }
  325. int decoded = decoder.GetChars(byteBuffer, 0, bytesRead, data, index);
  326. read += decoded;
  327. index += decoded;
  328. }
  329. return read;
  330. }
  331. /// <summary>
  332. /// Reads the specified number of bytes into the given buffer, starting at
  333. /// the given index.
  334. /// </summary>
  335. /// <param name="buffer">The buffer to copy data into</param>
  336. /// <param name="index">The first index to copy data into</param>
  337. /// <param name="count">The number of bytes to read</param>
  338. /// <returns>The number of bytes actually read. This will only be less than
  339. /// the requested number of bytes if the end of the stream is reached.
  340. /// </returns>
  341. public int Read(byte[] buffer, int index, int count)
  342. {
  343. CheckDisposed();
  344. if (buffer==null)
  345. {
  346. throw new ArgumentNullException("buffer");
  347. }
  348. if (index < 0)
  349. {
  350. throw new ArgumentOutOfRangeException("index");
  351. }
  352. if (count < 0)
  353. {
  354. throw new ArgumentOutOfRangeException("index");
  355. }
  356. if (count+index > buffer.Length)
  357. {
  358. throw new ArgumentException
  359. ("Not enough space in buffer for specified number of bytes starting at specified index");
  360. }
  361. int read=0;
  362. while (count > 0)
  363. {
  364. int block = stream.Read(buffer, index, count);
  365. if (block==0)
  366. {
  367. return read;
  368. }
  369. index += block;
  370. read += block;
  371. count -= block;
  372. }
  373. return read;
  374. }
  375. /// <summary>
  376. /// Reads the specified number of bytes, returning them in a new byte array.
  377. /// If not enough bytes are available before the end of the stream, this
  378. /// method will return what is available.
  379. /// </summary>
  380. /// <param name="count">The number of bytes to read</param>
  381. /// <returns>The bytes read</returns>
  382. public byte[] ReadBytes(int count)
  383. {
  384. CheckDisposed();
  385. if (count < 0)
  386. {
  387. throw new ArgumentOutOfRangeException("count");
  388. }
  389. byte[] ret = new byte[count];
  390. int index=0;
  391. while (index < count)
  392. {
  393. int read = stream.Read(ret, index, count-index);
  394. // Stream has finished half way through. That's fine, return what we've got.
  395. if (read==0)
  396. {
  397. byte[] copy = new byte[index];
  398. Buffer.BlockCopy(ret, 0, copy, 0, index);
  399. return copy;
  400. }
  401. index += read;
  402. }
  403. return ret;
  404. }
  405. /// <summary>
  406. /// Reads the specified number of bytes, returning them in a new byte array.
  407. /// If not enough bytes are available before the end of the stream, this
  408. /// method will throw an IOException.
  409. /// </summary>
  410. /// <param name="count">The number of bytes to read</param>
  411. /// <returns>The bytes read</returns>
  412. public byte[] ReadBytesOrThrow(int count)
  413. {
  414. byte[] ret = new byte[count];
  415. ReadInternal(ret, count);
  416. return ret;
  417. }
  418. /// <summary>
  419. /// Reads a 7-bit encoded integer from the stream. This is stored with the least significant
  420. /// information first, with 7 bits of information per byte of value, and the top
  421. /// bit as a continuation flag. This method is not affected by the endianness
  422. /// of the bit converter.
  423. /// </summary>
  424. /// <returns>The 7-bit encoded integer read from the stream.</returns>
  425. public int Read7BitEncodedInt()
  426. {
  427. CheckDisposed();
  428. int ret=0;
  429. for (int shift = 0; shift < 35; shift+=7)
  430. {
  431. int b = stream.ReadByte();
  432. if (b==-1)
  433. {
  434. throw new EndOfStreamException();
  435. }
  436. ret = ret | ((b&0x7f) << shift);
  437. if ((b & 0x80) == 0)
  438. {
  439. return ret;
  440. }
  441. }
  442. // Still haven't seen a byte with the high bit unset? Dodgy data.
  443. throw new IOException("Invalid 7-bit encoded integer in stream.");
  444. }
  445. /// <summary>
  446. /// Reads a 7-bit encoded integer from the stream. This is stored with the most significant
  447. /// information first, with 7 bits of information per byte of value, and the top
  448. /// bit as a continuation flag. This method is not affected by the endianness
  449. /// of the bit converter.
  450. /// </summary>
  451. /// <returns>The 7-bit encoded integer read from the stream.</returns>
  452. public int ReadBigEndian7BitEncodedInt()
  453. {
  454. CheckDisposed();
  455. int ret=0;
  456. for (int i=0; i < 5; i++)
  457. {
  458. int b = stream.ReadByte();
  459. if (b==-1)
  460. {
  461. throw new EndOfStreamException();
  462. }
  463. ret = (ret << 7) | (b&0x7f);
  464. if ((b & 0x80) == 0)
  465. {
  466. return ret;
  467. }
  468. }
  469. // Still haven't seen a byte with the high bit unset? Dodgy data.
  470. throw new IOException("Invalid 7-bit encoded integer in stream.");
  471. }
  472. /// <summary>
  473. /// Reads a length-prefixed string from the stream, using the encoding for this reader.
  474. /// A 7-bit encoded integer is first read, which specifies the number of bytes
  475. /// to read from the stream. These bytes are then converted into a string with
  476. /// the encoding for this reader.
  477. /// </summary>
  478. /// <returns>The string read from the stream.</returns>
  479. public string ReadString()
  480. {
  481. int bytesToRead = Read7BitEncodedInt();
  482. byte[] data = new byte[bytesToRead];
  483. ReadInternal(data, bytesToRead);
  484. return encoding.GetString(data, 0, data.Length);
  485. }
  486. #endregion
  487. #region Private methods
  488. /// <summary>
  489. /// Checks whether or not the reader has been disposed, throwing an exception if so.
  490. /// </summary>
  491. void CheckDisposed()
  492. {
  493. if (disposed)
  494. {
  495. throw new ObjectDisposedException("EndianBinaryReader");
  496. }
  497. }
  498. /// <summary>
  499. /// Reads the given number of bytes from the stream, throwing an exception
  500. /// if they can't all be read.
  501. /// </summary>
  502. /// <param name="data">Buffer to read into</param>
  503. /// <param name="size">Number of bytes to read</param>
  504. void ReadInternal (byte[] data, int size)
  505. {
  506. CheckDisposed();
  507. int index=0;
  508. while (index < size)
  509. {
  510. int read = stream.Read(data, index, size-index);
  511. if (read==0)
  512. {
  513. throw new EndOfStreamException
  514. (String.Format("End of stream reached with {0} byte{1} left to read.", size-index,
  515. size-index==1 ? "s" : ""));
  516. }
  517. index += read;
  518. }
  519. }
  520. /// <summary>
  521. /// Reads the given number of bytes from the stream if possible, returning
  522. /// the number of bytes actually read, which may be less than requested if
  523. /// (and only if) the end of the stream is reached.
  524. /// </summary>
  525. /// <param name="data">Buffer to read into</param>
  526. /// <param name="size">Number of bytes to read</param>
  527. /// <returns>Number of bytes actually read</returns>
  528. int TryReadInternal (byte[] data, int size)
  529. {
  530. CheckDisposed();
  531. int index=0;
  532. while (index < size)
  533. {
  534. int read = stream.Read(data, index, size-index);
  535. if (read==0)
  536. {
  537. return index;
  538. }
  539. index += read;
  540. }
  541. return index;
  542. }
  543. #endregion
  544. #region IDisposable Members
  545. /// <summary>
  546. /// Disposes of the underlying stream.
  547. /// </summary>
  548. public void Dispose()
  549. {
  550. if (!disposed)
  551. {
  552. disposed = true;
  553. ((IDisposable)stream).Dispose();
  554. }
  555. }
  556. #endregion
  557. }
  558. }