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

/MediaParsers.Desktop/BitTools.cs

https://github.com/slau/ManagedMediaHelpers
C# | 335 lines | 151 code | 41 blank | 143 comment | 40 complexity | ac961df2fd72028105106e40045927ab MD5 | raw file
  1. //-----------------------------------------------------------------------
  2. // <copyright file="BitTools.cs" company="Larry Olson">
  3. // (c) Copyright Larry Olson.
  4. // This source is subject to the Microsoft Public License (Ms-PL)
  5. // See http://code.msdn.microsoft.com/ManagedMediaHelpers/Project/License.aspx
  6. // All other rights reserved.
  7. // </copyright>
  8. //-----------------------------------------------------------------------
  9. namespace MediaParsers
  10. {
  11. using System;
  12. using System.Diagnostics;
  13. /// <summary>
  14. /// Helper methods for manipulating values at the byte and binary level.
  15. /// </summary>
  16. public static class BitTools
  17. {
  18. /// <summary>
  19. /// Defined by ID3v2 spec as 4 bytes
  20. /// </summary>
  21. private const int SyncSafeIntegerSize = 4;
  22. /// <summary>
  23. /// 1 Byte is 8 bits
  24. /// </summary>
  25. private const int ByteSize = 8;
  26. /// <summary>
  27. /// Masks out up to an integer sized (4 bytes) set of bits from an
  28. /// array of bytes.
  29. /// </summary>
  30. /// <param name="data">An array of data in Little Endian Order</param>
  31. /// <param name="firstBit">The bit index of the first bit</param>
  32. /// <param name="maskSize">The length of the mask in bits</param>
  33. /// <returns>An integer of the bits masked out</returns>
  34. public static int MaskBits(byte[] data, int firstBit, int maskSize)
  35. {
  36. // Guard against null data
  37. if (data == null)
  38. {
  39. throw new ArgumentNullException("data");
  40. }
  41. // Clear out numbers which are too small
  42. if (data.Length <= 0 || firstBit < 0 || maskSize <= 0)
  43. {
  44. throw new ArgumentException("data array, firstBit, or maskSize are too small");
  45. }
  46. // Clear out numbers where you are masking outside of the valid
  47. // range
  48. if ((firstBit + maskSize) > data.Length * ByteSize)
  49. {
  50. throw new ArgumentException("Attempting to mask outside of the data array");
  51. }
  52. // Clear out masks which are larger than the number of bits in an
  53. // int
  54. if (maskSize > sizeof(int) * ByteSize)
  55. {
  56. throw new ArgumentException("maskSize is larger than an integer");
  57. }
  58. // Figure out what byte the starting bit is in
  59. int startByteIndex = firstBit / ByteSize; // Int div
  60. // Figure what byte the ending bit is in
  61. int endByteIndex = (firstBit + maskSize - 1) / ByteSize; // Int div
  62. // initialize the mask
  63. int mask = 0;
  64. // Build an initial mask with the proper number of bits set to 1
  65. for (int i = 0; i < maskSize; i++)
  66. {
  67. mask |= 1 << i;
  68. }
  69. // initialize the return value
  70. long headerValue = 0;
  71. // initialize the bytes to be masked
  72. /*
  73. * The desired bits could be spread across 5 bytes
  74. * but they probably will be spread over fewer bytes
  75. */
  76. long temp;
  77. int shiftAmount;
  78. for (int bi = startByteIndex; bi <= endByteIndex; bi++)
  79. {
  80. temp = data[bi];
  81. shiftAmount = (endByteIndex - bi) * ByteSize;
  82. temp = temp << shiftAmount;
  83. headerValue = headerValue | temp;
  84. }
  85. // shift the bits to the right to make an int
  86. headerValue = headerValue >> (((ByteSize * (endByteIndex + 1)) - (firstBit + maskSize)) % 8);
  87. // mask out the appropriate bits
  88. headerValue = headerValue & mask;
  89. return (int)headerValue;
  90. }
  91. /// <summary>
  92. /// Converts a Syncronization Safe integer from the ID3v2 spec into a
  93. /// standard integer.
  94. /// </summary>
  95. /// <param name="syncSafeData">
  96. /// An array of bytes containing raw data in Syncronization Safe format
  97. /// as defined in the ID3v2 spec. This means that it is a 4 byte
  98. /// integer where the leading bit of each byte is a 0.
  99. /// For Example:
  100. /// 01111111 01111111 01111111 01111111
  101. /// Output would be:
  102. /// 00001111 11111111 11111111 11111111
  103. /// Assumes syncSafeData array is in Big Endiah Order.
  104. /// </param>
  105. /// <param name="startIndex">
  106. /// Where in the array of bytes, the syncsafe data starts. Note that
  107. /// data's size is assumed to be 4 bytes in length.
  108. /// </param>
  109. /// <returns>
  110. /// A standard integer. Note that this integer can only have a data
  111. /// resolution of 28 bits (max value of this could only be 2^28 -1).
  112. /// </returns>
  113. public static int ConvertSyncSafeToInt32(
  114. byte[] syncSafeData,
  115. int startIndex)
  116. {
  117. int integer = 0;
  118. int syncSafeByte = 0; // Store byte in an int to enable shifting
  119. int shiftAmount = 0;
  120. // Guard
  121. if (syncSafeData == null)
  122. {
  123. throw new ArgumentNullException("syncSafeData");
  124. }
  125. // Guard
  126. if (startIndex < 0 || startIndex >= syncSafeData.Length)
  127. {
  128. throw new ArgumentOutOfRangeException("startIndex", "startIndex is outside of the syncSafeData array");
  129. }
  130. // Guard
  131. if (syncSafeData.Length < 4)
  132. {
  133. throw new ArgumentException("syncSafeData array is smaller than an integer(4 bytes)", "syncSafeData");
  134. }
  135. // Guard
  136. if (startIndex + SyncSafeIntegerSize - 1 >= syncSafeData.Length)
  137. {
  138. throw new ArgumentOutOfRangeException("startIndex", "This startIndex is too close to the end of the data array");
  139. }
  140. // Shifts the first three bytes left and copies them into the int
  141. // Stop shifting before you hit the last byte. The last byte is
  142. // already where it needs to be
  143. int i;
  144. for (i = 0; i < SyncSafeIntegerSize - 1; i++)
  145. {
  146. syncSafeByte = syncSafeData[startIndex + i];
  147. shiftAmount = (ByteSize - 1) * (SyncSafeIntegerSize - 1 - i);
  148. integer |= syncSafeByte << shiftAmount;
  149. }
  150. // Copy the unshifted fourth bit into the int
  151. syncSafeByte = syncSafeData[startIndex + i];
  152. integer |= syncSafeByte;
  153. Debug.Assert(integer >= 0, "SyncSafeIntegers after conversion should always have the first 4 bits be 0 by spec and therefore cannot be negative");
  154. return integer;
  155. }
  156. /// <summary>
  157. /// Searches a byte array for a pattern of bits.
  158. /// </summary>
  159. /// <param name="data">
  160. /// The array of bytes to search for the pattern within.
  161. /// </param>
  162. /// <param name="pattern">
  163. /// The pattern of bytes to match with undesired bits zeroed out.
  164. /// </param>
  165. /// <param name="mask">
  166. /// A mask to zero out bits that aren't part of the pattern.
  167. /// </param>
  168. /// <param name="startIndex">
  169. /// The byte to begin the search from.
  170. /// </param>
  171. /// <returns>
  172. /// Returns the location of the first byte in the pattern or -1 if
  173. /// nothing was found or there was an error.
  174. /// </returns>
  175. public static int FindBitPattern(byte[] data, byte[] pattern, byte[] mask, int startIndex)
  176. {
  177. // GUARD
  178. if (data == null)
  179. {
  180. throw new ArgumentNullException("data");
  181. }
  182. // GUARD
  183. if (pattern == null)
  184. {
  185. throw new ArgumentNullException("pattern");
  186. }
  187. // GUARD
  188. if (mask == null)
  189. {
  190. throw new ArgumentNullException("mask");
  191. }
  192. // GUARD
  193. if (pattern.Length <= 0 || data.Length <= 0 || data.Length < pattern.Length || mask.Length != pattern.Length)
  194. {
  195. return -1;
  196. }
  197. // GUARD
  198. if (startIndex < 0 || startIndex >= data.Length)
  199. {
  200. throw new ArgumentOutOfRangeException("startIndex", "Start index must be in the range [0,data.Length-1]");
  201. }
  202. int di = startIndex; // data index
  203. int pati = 0; // pattern index
  204. while (di < data.Length)
  205. {
  206. if (pattern[pati] == (data[di] & mask[pati]))
  207. {
  208. pati++;
  209. }
  210. else if (pattern[pati] != (data[di] & mask[pati]))
  211. {
  212. pati = 0;
  213. }
  214. else
  215. {
  216. Debug.Assert(false, "All possible states should have already been covered.");
  217. }
  218. di++;
  219. if (pati == pattern.Length)
  220. {
  221. return di - pattern.Length;
  222. }
  223. }
  224. return -1;
  225. }
  226. /// <summary>
  227. /// Searches a byte array for a pattern of bits.
  228. /// </summary>
  229. /// <param name="data">
  230. /// The array of bytes to search for the pattern within.
  231. /// </param>
  232. /// <param name="pattern">
  233. /// The pattern of bytes to match with undesired bits zeroed out.
  234. /// </param>
  235. /// <param name="mask">
  236. /// A mask to zero out bits that aren't part of the pattern.
  237. /// </param>
  238. /// <returns>
  239. /// Returns the location of the first byte in the pattern or -1 if
  240. /// nothing was found or there was an error.
  241. /// </returns>
  242. public static int FindBitPattern(byte[] data, byte[] pattern, byte[] mask)
  243. {
  244. return FindBitPattern(data, pattern, mask, 0);
  245. }
  246. /// <summary>
  247. /// Searches a byte array for a pattern of bytes.
  248. /// </summary>
  249. /// <param name="data">
  250. /// The array of bytes to search for the pattern within.
  251. /// </param>
  252. /// <param name="pattern">
  253. /// The pattern of bytes to match.
  254. /// </param>
  255. /// <param name="startIndex">
  256. /// The byte to begin the search from.
  257. /// </param>
  258. /// <returns>
  259. /// Returns the location of the first byte in the pattern or -1 if
  260. /// nothing was found or there was an error.
  261. /// </returns>
  262. public static int FindBytePattern(byte[] data, byte[] pattern, int startIndex)
  263. {
  264. // GUARD
  265. if (pattern == null)
  266. {
  267. throw new ArgumentNullException("pattern");
  268. }
  269. byte[] mask = new byte[pattern.Length];
  270. for (int i = 0; i < pattern.Length; i++)
  271. {
  272. mask[i] = byte.MaxValue; // 1111 1111
  273. }
  274. return FindBitPattern(data, pattern, mask, startIndex);
  275. }
  276. /// <summary>
  277. /// Searches a byte array for a pattern of bytes.
  278. /// </summary>
  279. /// <param name="data">
  280. /// The array of bytes to search for the pattern within.
  281. /// </param>
  282. /// <param name="pattern">
  283. /// The pattern of bytes to match.
  284. /// </param>
  285. /// <returns>
  286. /// Returns the location of the first byte in the pattern or -1 if
  287. /// nothing was found or there was an error.
  288. /// </returns>
  289. public static int FindBytePattern(byte[] data, byte[] pattern)
  290. {
  291. return FindBytePattern(data, pattern, 0);
  292. }
  293. }
  294. }