erlang自定义二进制协议
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
10 KiB

преди 1 година
  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.BinaryWriter, but with either endianness, depending on
  9. /// the EndianBitConverter it is constructed with.
  10. /// </summary>
  11. public class EndianBinaryWriter : IDisposable
  12. {
  13. #region Fields not directly related to properties
  14. /// <summary>
  15. /// Whether or not this writer has been disposed yet.
  16. /// </summary>
  17. bool disposed=false;
  18. /// <summary>
  19. /// Buffer used for temporary storage during conversion from primitives
  20. /// </summary>
  21. byte[] buffer = new byte[16];
  22. /// <summary>
  23. /// Buffer used for Write(char)
  24. /// </summary>
  25. char[] charBuffer = new char[1];
  26. #endregion
  27. #region Constructors
  28. /// <summary>
  29. /// Constructs a new binary writer with the given bit converter, writing
  30. /// to the given stream, using UTF-8 encoding.
  31. /// </summary>
  32. /// <param name="bitConverter">Converter to use when writing data</param>
  33. /// <param name="stream">Stream to write data to</param>
  34. public EndianBinaryWriter (EndianBitConverter bitConverter,
  35. Stream stream) : this (bitConverter, stream, Encoding.UTF8)
  36. {
  37. }
  38. /// <summary>
  39. /// Constructs a new binary writer with the given bit converter, writing
  40. /// to the given stream, using the given encoding.
  41. /// </summary>
  42. /// <param name="bitConverter">Converter to use when writing data</param>
  43. /// <param name="stream">Stream to write data to</param>
  44. /// <param name="encoding">Encoding to use when writing character data</param>
  45. public EndianBinaryWriter (EndianBitConverter bitConverter, Stream stream, Encoding encoding)
  46. {
  47. if (bitConverter==null)
  48. {
  49. throw new ArgumentNullException("bitConverter");
  50. }
  51. if (stream==null)
  52. {
  53. throw new ArgumentNullException("stream");
  54. }
  55. if (encoding==null)
  56. {
  57. throw new ArgumentNullException("encoding");
  58. }
  59. if (!stream.CanWrite)
  60. {
  61. throw new ArgumentException("Stream isn't writable", "stream");
  62. }
  63. this.stream = stream;
  64. this.bitConverter = bitConverter;
  65. this.encoding = encoding;
  66. }
  67. #endregion
  68. #region Properties
  69. EndianBitConverter bitConverter;
  70. /// <summary>
  71. /// The bit converter used to write values to the stream
  72. /// </summary>
  73. public EndianBitConverter BitConverter
  74. {
  75. get { return bitConverter; }
  76. }
  77. Encoding encoding;
  78. /// <summary>
  79. /// The encoding used to write strings
  80. /// </summary>
  81. public Encoding Encoding
  82. {
  83. get { return encoding; }
  84. }
  85. Stream stream;
  86. /// <summary>
  87. /// Gets the underlying stream of the EndianBinaryWriter.
  88. /// </summary>
  89. public Stream BaseStream
  90. {
  91. get { return stream; }
  92. }
  93. #endregion
  94. #region Public methods
  95. /// <summary>
  96. /// Closes the writer, including the underlying stream.
  97. /// </summary>
  98. public void Close()
  99. {
  100. Dispose();
  101. }
  102. /// <summary>
  103. /// Flushes the underlying stream.
  104. /// </summary>
  105. public void Flush()
  106. {
  107. CheckDisposed();
  108. stream.Flush();
  109. }
  110. /// <summary>
  111. /// Seeks within the stream.
  112. /// </summary>
  113. /// <param name="offset">Offset to seek to.</param>
  114. /// <param name="origin">Origin of seek operation.</param>
  115. public void Seek (int offset, SeekOrigin origin)
  116. {
  117. CheckDisposed();
  118. stream.Seek (offset, origin);
  119. }
  120. /// <summary>
  121. /// Writes a boolean value to the stream. 1 byte is written.
  122. /// </summary>
  123. /// <param name="value">The value to write</param>
  124. public void Write (bool value)
  125. {
  126. bitConverter.CopyBytes(value, buffer, 0);
  127. WriteInternal(buffer, 1);
  128. }
  129. /// <summary>
  130. /// Writes a 16-bit signed integer to the stream, using the bit converter
  131. /// for this writer. 2 bytes are written.
  132. /// </summary>
  133. /// <param name="value">The value to write</param>
  134. public void Write (short value)
  135. {
  136. bitConverter.CopyBytes(value, buffer, 0);
  137. WriteInternal(buffer, 2);
  138. }
  139. /// <summary>
  140. /// Writes a 32-bit signed integer to the stream, using the bit converter
  141. /// for this writer. 4 bytes are written.
  142. /// </summary>
  143. /// <param name="value">The value to write</param>
  144. public void Write (int value)
  145. {
  146. bitConverter.CopyBytes(value, buffer, 0);
  147. WriteInternal(buffer, 4);
  148. }
  149. /// <summary>
  150. /// Writes a 64-bit signed integer to the stream, using the bit converter
  151. /// for this writer. 8 bytes are written.
  152. /// </summary>
  153. /// <param name="value">The value to write</param>
  154. public void Write (long value)
  155. {
  156. bitConverter.CopyBytes(value, buffer, 0);
  157. WriteInternal(buffer, 8);
  158. }
  159. /// <summary>
  160. /// Writes a 16-bit unsigned integer to the stream, using the bit converter
  161. /// for this writer. 2 bytes are written.
  162. /// </summary>
  163. /// <param name="value">The value to write</param>
  164. public void Write (ushort value)
  165. {
  166. bitConverter.CopyBytes(value, buffer, 0);
  167. WriteInternal(buffer, 2);
  168. }
  169. /// <summary>
  170. /// Writes a 32-bit unsigned integer to the stream, using the bit converter
  171. /// for this writer. 4 bytes are written.
  172. /// </summary>
  173. /// <param name="value">The value to write</param>
  174. public void Write (uint value)
  175. {
  176. bitConverter.CopyBytes(value, buffer, 0);
  177. WriteInternal(buffer, 4);
  178. }
  179. /// <summary>
  180. /// Writes a 64-bit unsigned integer to the stream, using the bit converter
  181. /// for this writer. 8 bytes are written.
  182. /// </summary>
  183. /// <param name="value">The value to write</param>
  184. public void Write (ulong value)
  185. {
  186. bitConverter.CopyBytes(value, buffer, 0);
  187. WriteInternal(buffer, 8);
  188. }
  189. /// <summary>
  190. /// Writes a single-precision floating-point value to the stream, using the bit converter
  191. /// for this writer. 4 bytes are written.
  192. /// </summary>
  193. /// <param name="value">The value to write</param>
  194. public void Write (float value)
  195. {
  196. bitConverter.CopyBytes(value, buffer, 0);
  197. WriteInternal(buffer, 4);
  198. }
  199. /// <summary>
  200. /// Writes a double-precision floating-point value to the stream, using the bit converter
  201. /// for this writer. 8 bytes are written.
  202. /// </summary>
  203. /// <param name="value">The value to write</param>
  204. public void Write (double value)
  205. {
  206. bitConverter.CopyBytes(value, buffer, 0);
  207. WriteInternal(buffer, 8);
  208. }
  209. /// <summary>
  210. /// Writes a decimal value to the stream, using the bit converter for this writer.
  211. /// 16 bytes are written.
  212. /// </summary>
  213. /// <param name="value">The value to write</param>
  214. public void Write (decimal value)
  215. {
  216. bitConverter.CopyBytes(value, buffer, 0);
  217. WriteInternal(buffer, 16);
  218. }
  219. /// <summary>
  220. /// Writes a signed byte to the stream.
  221. /// </summary>
  222. /// <param name="value">The value to write</param>
  223. public void Write (byte value)
  224. {
  225. buffer[0] = value;
  226. WriteInternal(buffer, 1);
  227. }
  228. /// <summary>
  229. /// Writes an unsigned byte to the stream.
  230. /// </summary>
  231. /// <param name="value">The value to write</param>
  232. public void Write (sbyte value)
  233. {
  234. buffer[0] = unchecked((byte)value);
  235. WriteInternal(buffer, 1);
  236. }
  237. /// <summary>
  238. /// Writes an array of bytes to the stream.
  239. /// </summary>
  240. /// <param name="value">The values to write</param>
  241. public void Write (byte[] value)
  242. {
  243. if (value == null)
  244. {
  245. throw (new System.ArgumentNullException("value"));
  246. }
  247. WriteInternal(value, value.Length);
  248. }
  249. /// <summary>
  250. /// Writes a portion of an array of bytes to the stream.
  251. /// </summary>
  252. /// <param name="value">An array containing the bytes to write</param>
  253. /// <param name="offset">The index of the first byte to write within the array</param>
  254. /// <param name="count">The number of bytes to write</param>
  255. public void Write (byte[] value, int offset, int count)
  256. {
  257. CheckDisposed();
  258. stream.Write(value, offset, count);
  259. }
  260. /// <summary>
  261. /// Writes a single character to the stream, using the encoding for this writer.
  262. /// </summary>
  263. /// <param name="value">The value to write</param>
  264. public void Write(char value)
  265. {
  266. charBuffer[0] = value;
  267. Write(charBuffer);
  268. }
  269. /// <summary>
  270. /// Writes an array of characters to the stream, using the encoding for this writer.
  271. /// </summary>
  272. /// <param name="value">An array containing the characters to write</param>
  273. public void Write(char[] value)
  274. {
  275. if (value==null)
  276. {
  277. throw new ArgumentNullException("value");
  278. }
  279. CheckDisposed();
  280. byte[] data = Encoding.GetBytes(value, 0, value.Length);
  281. WriteInternal(data, data.Length);
  282. }
  283. /// <summary>
  284. /// Writes a string to the stream, using the encoding for this writer.
  285. /// </summary>
  286. /// <param name="value">The value to write. Must not be null.</param>
  287. /// <exception cref="ArgumentNullException">value is null</exception>
  288. public void Write(string value)
  289. {
  290. if (value==null)
  291. {
  292. throw new ArgumentNullException("value");
  293. }
  294. CheckDisposed();
  295. byte[] data = Encoding.GetBytes(value);
  296. Write7BitEncodedInt(data.Length);
  297. WriteInternal(data, data.Length);
  298. }
  299. /// <summary>
  300. /// Writes a 7-bit encoded integer from the stream. This is stored with the least significant
  301. /// information first, with 7 bits of information per byte of value, and the top
  302. /// bit as a continuation flag.
  303. /// </summary>
  304. /// <param name="value">The 7-bit encoded integer to write to the stream</param>
  305. public void Write7BitEncodedInt(int value)
  306. {
  307. CheckDisposed();
  308. if (value < 0)
  309. {
  310. throw new ArgumentOutOfRangeException("value", "Value must be greater than or equal to 0.");
  311. }
  312. int index=0;
  313. while (value >= 128)
  314. {
  315. buffer[index++]= (byte)((value&0x7f) | 0x80);
  316. value = value >> 7;
  317. index++;
  318. }
  319. buffer[index++]=(byte)value;
  320. stream.Write(buffer, 0, index);
  321. }
  322. #endregion
  323. #region Private methods
  324. /// <summary>
  325. /// Checks whether or not the writer has been disposed, throwing an exception if so.
  326. /// </summary>
  327. void CheckDisposed()
  328. {
  329. if (disposed)
  330. {
  331. throw new ObjectDisposedException("EndianBinaryWriter");
  332. }
  333. }
  334. /// <summary>
  335. /// Writes the specified number of bytes from the start of the given byte array,
  336. /// after checking whether or not the writer has been disposed.
  337. /// </summary>
  338. /// <param name="bytes">The array of bytes to write from</param>
  339. /// <param name="length">The number of bytes to write</param>
  340. void WriteInternal (byte[] bytes, int length)
  341. {
  342. CheckDisposed();
  343. stream.Write(bytes, 0, length);
  344. }
  345. #endregion
  346. #region IDisposable Members
  347. /// <summary>
  348. /// Disposes of the underlying stream.
  349. /// </summary>
  350. public void Dispose()
  351. {
  352. if (!disposed)
  353. {
  354. Flush();
  355. disposed = true;
  356. ((IDisposable)stream).Dispose();
  357. }
  358. }
  359. #endregion
  360. }
  361. }