PageRenderTime 49ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/1.3/Source/Rxx/Parsers/BinaryParser.cs

#
C# | 481 lines | 329 code | 62 blank | 90 comment | 50 complexity | 5699b8bf937394a4dcf64855eb4cbb4e MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.Contracts;
  4. using System.Linq;
  5. using System.Text;
  6. using Rxx.Parsers.Linq;
  7. namespace Rxx.Parsers
  8. {
  9. /// <summary>
  10. /// Represents a parser over an enumerable sequence of bytes.
  11. /// </summary>
  12. /// <typeparam name="TResult">The type of the elements that are generated from parsing the sequence of bytes.</typeparam>
  13. public abstract class BinaryParser<TResult> : Parser<byte, TResult>, IBinaryParser<TResult>
  14. {
  15. #region Public Properties
  16. #endregion
  17. #region Private / Protected
  18. /// <summary>
  19. /// Gets a parser with a grammar that matches a single byte and converts it into a boolean value.
  20. /// </summary>
  21. protected IParser<byte, bool> Boolean
  22. {
  23. get
  24. {
  25. Contract.Ensures(Contract.Result<IParser<byte, bool>>() != null);
  26. return Next.Select(value => BitConverter.ToBoolean(new[] { value }, 0));
  27. }
  28. }
  29. /// <summary>
  30. /// Gets a parser with a grammar that matches a single byte.
  31. /// </summary>
  32. protected IParser<byte, byte> Byte
  33. {
  34. get
  35. {
  36. Contract.Ensures(Contract.Result<IParser<byte, byte>>() != null);
  37. return Next;
  38. }
  39. }
  40. /// <summary>
  41. /// Gets a parser with a grammar that matches a single byte as a signed byte.
  42. /// </summary>
  43. [CLSCompliant(false)]
  44. protected IParser<byte, sbyte> SByte
  45. {
  46. get
  47. {
  48. Contract.Ensures(Contract.Result<IParser<byte, sbyte>>() != null);
  49. return Next.Select(b => (sbyte) b);
  50. }
  51. }
  52. /// <summary>
  53. /// Gets a parser with a grammar that matches two bytes and converts them into an integer.
  54. /// </summary>
  55. protected IParser<byte, short> Int16
  56. {
  57. get
  58. {
  59. Contract.Ensures(Contract.Result<IParser<byte, short>>() != null);
  60. return Next.Exactly(2).Select(bytes => BitConverter.ToInt16(Order(bytes).ToArray(), 0));
  61. }
  62. }
  63. /// <summary>
  64. /// Gets a parser with a grammar that matches two bytes and converts them into an unsigned integer.
  65. /// </summary>
  66. [CLSCompliant(false)]
  67. protected IParser<byte, ushort> UInt16
  68. {
  69. get
  70. {
  71. Contract.Ensures(Contract.Result<IParser<byte, ushort>>() != null);
  72. return Next.Exactly(2).Select(bytes => BitConverter.ToUInt16(Order(bytes).ToArray(), 0));
  73. }
  74. }
  75. /// <summary>
  76. /// Gets a parser with a grammar that matches four bytes and converts them into an integer.
  77. /// </summary>
  78. protected IParser<byte, int> Int32
  79. {
  80. get
  81. {
  82. Contract.Ensures(Contract.Result<IParser<byte, int>>() != null);
  83. return Next.Exactly(4).Select(bytes => BitConverter.ToInt32(Order(bytes).ToArray(), 0));
  84. }
  85. }
  86. /// <summary>
  87. /// Gets a parser with a grammar that matches four bytes and converts them into an unsigned integer.
  88. /// </summary>
  89. [CLSCompliant(false)]
  90. protected IParser<byte, uint> UInt32
  91. {
  92. get
  93. {
  94. Contract.Ensures(Contract.Result<IParser<byte, uint>>() != null);
  95. return Next.Exactly(4).Select(bytes => BitConverter.ToUInt32(Order(bytes).ToArray(), 0));
  96. }
  97. }
  98. /// <summary>
  99. /// Gets a parser with a grammar that matches eight bytes and converts them into an integer.
  100. /// </summary>
  101. protected IParser<byte, long> Int64
  102. {
  103. get
  104. {
  105. Contract.Ensures(Contract.Result<IParser<byte, long>>() != null);
  106. return Next.Exactly(8).Select(bytes => BitConverter.ToInt64(Order(bytes).ToArray(), 0));
  107. }
  108. }
  109. /// <summary>
  110. /// Gets a parser with a grammar that matches eight bytes and converts them into an unsigned integer.
  111. /// </summary>
  112. [CLSCompliant(false)]
  113. protected IParser<byte, ulong> UInt64
  114. {
  115. get
  116. {
  117. Contract.Ensures(Contract.Result<IParser<byte, ulong>>() != null);
  118. return Next.Exactly(8).Select(bytes => BitConverter.ToUInt64(Order(bytes).ToArray(), 0));
  119. }
  120. }
  121. /// <summary>
  122. /// Gets a parser with a grammar that matches four bytes and converts them into a floating point number.
  123. /// </summary>
  124. protected IParser<byte, float> Single
  125. {
  126. get
  127. {
  128. Contract.Ensures(Contract.Result<IParser<byte, float>>() != null);
  129. return Next.Exactly(4).Select(bytes => BitConverter.ToSingle(Order(bytes).ToArray(), 0));
  130. }
  131. }
  132. /// <summary>
  133. /// Gets a parser with a grammar that matches eight bytes and converts them into a floating point number.
  134. /// </summary>
  135. protected IParser<byte, double> Double
  136. {
  137. get
  138. {
  139. Contract.Ensures(Contract.Result<IParser<byte, double>>() != null);
  140. return Next.Exactly(8).Select(bytes => BitConverter.ToDouble(Order(bytes).ToArray(), 0));
  141. }
  142. }
  143. /// <summary>
  144. /// Gets a parser with a grammar that matches two bytes and converts them into a unicode character.
  145. /// </summary>
  146. protected IParser<byte, char> Char
  147. {
  148. get
  149. {
  150. Contract.Ensures(Contract.Result<IParser<byte, char>>() != null);
  151. return Next.Exactly(2).Select(bytes => BitConverter.ToChar(Order(bytes).ToArray(), 0));
  152. }
  153. }
  154. private readonly bool bigEndian;
  155. #endregion
  156. #region Constructors
  157. /// <summary>
  158. /// Constructs a new instance of the <see cref="BinaryParser{TResult}" /> class for derived classes
  159. /// to parse sequences with little endian byte order.
  160. /// </summary>
  161. protected BinaryParser()
  162. {
  163. }
  164. /// <summary>
  165. /// Constructs a new instance of the <see cref="BinaryParser{TResult}" /> class for derived classes
  166. /// to parse sequences with the specified byte order.
  167. /// </summary>
  168. /// <param name="bigEndian">Specifies whether the byte order of sequences being parsed is big endian
  169. /// or little endian. The default value is <see langword="false"/>, which indicates that sequences are
  170. /// encoded with a little endian byte order, which is the default byte order in .NET when running on
  171. /// a Windows OS.</param>
  172. protected BinaryParser(bool bigEndian)
  173. {
  174. this.bigEndian = bigEndian;
  175. }
  176. #endregion
  177. #region Methods
  178. internal static IEnumerable<byte> Order(IEnumerable<byte> bytes, bool bigEndian)
  179. {
  180. Contract.Requires(bytes != null);
  181. Contract.Ensures(Contract.Result<IEnumerable<byte>>() != null);
  182. Contract.Ensures(Contract.Result<IEnumerable<byte>>().Count() == bytes.Count());
  183. return bigEndian == !BitConverter.IsLittleEndian ? bytes : bytes.Reverse();
  184. }
  185. /// <summary>
  186. /// Orders the specified sequence of bytes according to the endianness of the parser and the
  187. /// endianness of the host platform.
  188. /// </summary>
  189. /// <param name="bytes">The sequence of bytes to be ordered.</param>
  190. /// <returns>The specified <paramref name="bytes"/> in reverse order, if the endianness of the
  191. /// parser does not match the host platform; otherwise, <paramref name="bytes"/> is returned
  192. /// unmodified.</returns>
  193. protected IEnumerable<byte> Order(IEnumerable<byte> bytes)
  194. {
  195. Contract.Requires(bytes != null);
  196. Contract.Ensures(Contract.Result<IEnumerable<byte>>() != null);
  197. Contract.Ensures(Contract.Result<IEnumerable<byte>>().Count() == bytes.Count());
  198. return Order(bytes, bigEndian);
  199. }
  200. /// <summary>
  201. /// Creates a parser with a grammar that matches the specified number of bytes and converts them
  202. /// into a string of hyphen-delimited hexadecimal pairs representing each byte.
  203. /// </summary>
  204. /// <param name="length">The number of bytes to be converted.</param>
  205. /// <returns>A parser with a grammar that matches the specified number of bytes and converts them
  206. /// into a string of hyphen-delimited hexadecimal pairs representing each byte..</returns>
  207. protected IParser<byte, string> HexString(int length)
  208. {
  209. Contract.Requires(length >= 0);
  210. Contract.Ensures(Contract.Result<IParser<byte, string>>() != null);
  211. return Next.Exactly(length).Select(bytes => BitConverter.ToString(Order(bytes).ToArray()));
  212. }
  213. /// <summary>
  214. /// Creates a parser with a grammar that matches the specified number of bytes and converts them
  215. /// into a string in the specified <paramref name="encoding"/>.
  216. /// </summary>
  217. /// <param name="encoding">The character encoding for converting the bytes into a string.</param>
  218. /// <param name="length">The number of bytes to be converted.</param>
  219. /// <returns>A parser with a grammar that matches the specified number of bytes and converts them
  220. /// into a string in the specified <paramref name="encoding"/>.</returns>
  221. protected IParser<byte, string> String(Encoding encoding, int length)
  222. {
  223. Contract.Requires(encoding != null);
  224. Contract.Requires(length >= 0);
  225. Contract.Ensures(Contract.Result<IParser<byte, string>>() != null);
  226. return from bytes in Next.Exactly(length)
  227. let array = Order(bytes).ToArray()
  228. select encoding.GetString(array, 0, array.Length);
  229. }
  230. /// <summary>
  231. /// Creates a parser with a grammar that returns a string in the specified <paramref name="encoding"/>
  232. /// and with a length that is specified by a 7-bit encoded prefix, which is written by the
  233. /// <see cref="System.IO.BinaryWriter.Write7BitEncodedInt"/> method.
  234. /// </summary>
  235. /// <param name="encoding">The character encoding for converting the bytes into a string.</param>
  236. /// <returns>A parser with a grammar that returns a string in the specified <paramref name="encoding"/>
  237. /// and with a length that is specified by a 7-bit encoded prefix.</returns>
  238. protected IParser<byte, string> String(Encoding encoding)
  239. {
  240. Contract.Requires(encoding != null);
  241. Contract.Ensures(Contract.Result<IParser<byte, string>>() != null);
  242. return from length in Parse7BitEncodedInt32()
  243. from bytes in Next.Exactly(length)
  244. let array = Order(bytes).ToArray()
  245. select encoding.GetString(array, 0, array.Length);
  246. }
  247. private IParser<byte, int> Parse7BitEncodedInt32()
  248. {
  249. Contract.Ensures(Contract.Result<IParser<byte, int>>() != null);
  250. return from b1 in Next
  251. from length in
  252. (b1 & 0x80) != 0
  253. ? from b2 in Next
  254. from length2 in
  255. (b2 & 0x80) != 0
  256. ? from b3 in Next
  257. from length3 in
  258. (b3 & 0x80) != 0
  259. ? from b4 in Next
  260. where (b4 & 0x80) == 0
  261. select (b1 & 0x7f) | ((b2 & 0x7f) << 7) | ((b3 & 0x7f) << 14) | (b4 << 21)
  262. : Yield((b1 & 0x7f) | ((b2 & 0x7f) << 7) | (b3 << 14))
  263. select length3
  264. : Yield((b1 & 0x7f) | (b2 << 7))
  265. select length2
  266. : Yield(b1)
  267. select length;
  268. }
  269. private IParser<byte, int> Yield(int value)
  270. {
  271. Contract.Ensures(Contract.Result<IParser<byte, int>>() != null);
  272. return this.Yield(_ => ParseResult.Return(value, 0));
  273. }
  274. /// <summary>
  275. /// Creates a parser with a grammar that matches the number of bytes in the specified
  276. /// enum type and converts them into an instance of the enum.
  277. /// </summary>
  278. /// <typeparam name="TEnum">The type of the enum value to be parsed.</typeparam>
  279. /// <returns>A parser with a grammar that matches the number of bytes in the specified
  280. /// enum type and converts them into an instance of the enum.</returns>
  281. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Contracts", "RequiresAtCall-typeof(TEnum).IsEnum",
  282. Justification = "The static checker doesn't understand typeof(TEnum).IsEnum.")]
  283. protected IParser<byte, TEnum> Enum<TEnum>()
  284. where TEnum : struct
  285. {
  286. Contract.Requires(typeof(TEnum).IsEnum);
  287. Contract.Ensures(Contract.Result<IParser<byte, TEnum>>() != null);
  288. var type = typeof(TEnum);
  289. var underlyingType = System.Enum.GetUnderlyingType(type);
  290. if (underlyingType == typeof(int) || underlyingType == typeof(uint))
  291. {
  292. return this.Int32.Select(value => (TEnum) System.Enum.ToObject(type, value));
  293. }
  294. else if (underlyingType == typeof(long) || underlyingType == typeof(ulong))
  295. {
  296. return this.Int64.Select(value => (TEnum) System.Enum.ToObject(type, value));
  297. }
  298. else if (underlyingType == typeof(short) || underlyingType == typeof(ushort))
  299. {
  300. return this.Int16.Select(value => (TEnum) System.Enum.ToObject(type, value));
  301. }
  302. else if (underlyingType == typeof(byte) || underlyingType == typeof(sbyte))
  303. {
  304. return this.Byte.Select(value => (TEnum) System.Enum.ToObject(type, value));
  305. }
  306. else
  307. {
  308. throw new NotSupportedException();
  309. }
  310. }
  311. #endregion
  312. #region IBinaryParser<TResult> Members
  313. IParser<byte, bool> IBinaryParser<TResult>.Boolean
  314. {
  315. get
  316. {
  317. return Boolean;
  318. }
  319. }
  320. IParser<byte, byte> IBinaryParser<TResult>.Byte
  321. {
  322. get
  323. {
  324. return Byte;
  325. }
  326. }
  327. IParser<byte, sbyte> IBinaryParser<TResult>.SByte
  328. {
  329. get
  330. {
  331. return SByte;
  332. }
  333. }
  334. IParser<byte, short> IBinaryParser<TResult>.Int16
  335. {
  336. get
  337. {
  338. return Int16;
  339. }
  340. }
  341. IParser<byte, ushort> IBinaryParser<TResult>.UInt16
  342. {
  343. get
  344. {
  345. return UInt16;
  346. }
  347. }
  348. IParser<byte, int> IBinaryParser<TResult>.Int32
  349. {
  350. get
  351. {
  352. return Int32;
  353. }
  354. }
  355. IParser<byte, uint> IBinaryParser<TResult>.UInt32
  356. {
  357. get
  358. {
  359. return UInt32;
  360. }
  361. }
  362. IParser<byte, long> IBinaryParser<TResult>.Int64
  363. {
  364. get
  365. {
  366. return Int64;
  367. }
  368. }
  369. IParser<byte, ulong> IBinaryParser<TResult>.UInt64
  370. {
  371. get
  372. {
  373. return UInt64;
  374. }
  375. }
  376. IParser<byte, float> IBinaryParser<TResult>.Single
  377. {
  378. get
  379. {
  380. return Single;
  381. }
  382. }
  383. IParser<byte, double> IBinaryParser<TResult>.Double
  384. {
  385. get
  386. {
  387. return Double;
  388. }
  389. }
  390. IParser<byte, char> IBinaryParser<TResult>.Char
  391. {
  392. get
  393. {
  394. return Char;
  395. }
  396. }
  397. IEnumerable<byte> IBinaryParser<TResult>.Order(IEnumerable<byte> bytes)
  398. {
  399. return Order(bytes);
  400. }
  401. IParser<byte, string> IBinaryParser<TResult>.HexString(int length)
  402. {
  403. return HexString(length);
  404. }
  405. IParser<byte, string> IBinaryParser<TResult>.String(Encoding encoding, int length)
  406. {
  407. return String(encoding, length);
  408. }
  409. IParser<byte, string> IBinaryParser<TResult>.String(Encoding encoding)
  410. {
  411. return String(encoding);
  412. }
  413. IParser<byte, TEnum> IBinaryParser<TResult>.Enum<TEnum>()
  414. {
  415. return Enum<TEnum>();
  416. }
  417. #endregion
  418. }
  419. }