BinaryConvert5Bit.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace Wayne.Lib
  5. {
  6. /// <summary>
  7. /// Binary Encoder/Decoder with the custom 5-bit-per-byte format that
  8. /// is especially good for screen viewing, since the characters that
  9. /// may be confused has been eliminated (I/1/l/O/0). The purpose is
  10. /// to encode binary data into a string representation.
  11. /// </summary>
  12. public class BinaryConvert5Bit
  13. {
  14. /// <summary>
  15. /// Vadildates if a given set of characters are valid.
  16. /// </summary>
  17. /// <param name="s"></param>
  18. /// <returns></returns>
  19. public bool Valid5BitString(IEnumerable<char> s)
  20. {
  21. const string allowedChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
  22. foreach (char character in s)
  23. {
  24. if (allowedChars.IndexOf(character) < 0)
  25. return false;
  26. }
  27. return true;
  28. }
  29. /// <summary>
  30. /// Returns the string representation of the bytes
  31. /// </summary>
  32. /// <param name="bytes"></param>
  33. /// <returns></returns>
  34. public string ToString(byte[] bytes)
  35. {
  36. byte[] fivebitBytes = EncodeFiveBitPerBytesArray(bytes);
  37. string encodedString = ConvertToString(fivebitBytes);
  38. return encodedString;
  39. }
  40. private string ConvertToString(byte[] bytes)
  41. {
  42. StringBuilder sb = new StringBuilder();
  43. foreach (var b in bytes)
  44. {
  45. sb.Append(ConvertByte(b));
  46. }
  47. return sb.ToString();
  48. }
  49. private char ConvertByte(byte b)
  50. {
  51. switch (b)
  52. {
  53. case 0: return '2';
  54. case 1: return '3';
  55. case 2: return '4';
  56. case 3: return '5';
  57. case 4: return '6';
  58. case 5: return '7';
  59. case 6: return '8';
  60. case 7: return '9';
  61. case 8: return 'A';
  62. case 9: return 'B';
  63. case 10: return 'C';
  64. case 11: return 'D';
  65. case 12: return 'E';
  66. case 13: return 'F';
  67. case 14: return 'G';
  68. case 15: return 'H';
  69. case 16: return 'J';
  70. case 17: return 'K';
  71. case 18: return 'L';
  72. case 19: return 'M';
  73. case 20: return 'N';
  74. case 21: return 'P';
  75. case 22: return 'Q';
  76. case 23: return 'R';
  77. case 24: return 'S';
  78. case 25: return 'T';
  79. case 26: return 'U';
  80. case 27: return 'V';
  81. case 28: return 'W';
  82. case 29: return 'X';
  83. case 30: return 'Y';
  84. case 31: return 'Z';
  85. }
  86. throw new ArgumentException(string.Format("Invalid byte {0} it contains more than 5 bits.", b));
  87. }
  88. private byte ConvertChar(char c)
  89. {
  90. switch (c)
  91. {
  92. case '2': return 0;
  93. case '3': return 1;
  94. case '4': return 2;
  95. case '5': return 3;
  96. case '6': return 4;
  97. case '7': return 5;
  98. case '8': return 6;
  99. case '9': return 7;
  100. case 'A': return 8;
  101. case 'B': return 9;
  102. case 'C': return 10;
  103. case 'D': return 11;
  104. case 'E': return 12;
  105. case 'F': return 13;
  106. case 'G': return 14;
  107. case 'H': return 15;
  108. case 'J': return 16;
  109. case 'K': return 17;
  110. case 'L': return 18;
  111. case 'M': return 19;
  112. case 'N': return 20;
  113. case 'P': return 21;
  114. case 'Q': return 22;
  115. case 'R': return 23;
  116. case 'S': return 24;
  117. case 'T': return 25;
  118. case 'U': return 26;
  119. case 'V': return 27;
  120. case 'W': return 28;
  121. case 'X': return 29;
  122. case 'Y': return 30;
  123. case 'Z': return 31;
  124. }
  125. throw new ArgumentException(string.Format("Invalid char {0}.", c));
  126. }
  127. private byte[] EncodeFiveBitPerBytesArray(byte[] bytes)
  128. {
  129. byte[] inputBytes = (byte[])bytes.Clone();
  130. //Calculate resulting number of bytes
  131. int inputBitCount = inputBytes.Length * 8;
  132. int resultingBytesCount = inputBitCount / 5;
  133. if (inputBitCount % 5 > 0)
  134. resultingBytesCount++;
  135. byte[] resultBytes = new byte[resultingBytesCount];
  136. for (int resultByteIndex = resultingBytesCount - 1; resultByteIndex >= 0; resultByteIndex--)
  137. {
  138. // Console.WriteLine(ByteListToStringBinary(inputBytes));
  139. //Get the last five bits of the last byte in the byte array.
  140. byte fiveBits = (byte)(inputBytes[inputBytes.Length - 1] & 0x1f);
  141. resultBytes[resultByteIndex] = fiveBits;
  142. //Shift all the bits five places to the right
  143. for (int inputByteIndex = inputBytes.Length - 1; inputByteIndex >= 0; inputByteIndex--)
  144. {
  145. inputBytes[inputByteIndex] = (byte)(inputBytes[inputByteIndex] >> 5);//Shift the remaining 3 bits to the right
  146. if (inputByteIndex > 0)//If we are at the leftmost byte, there is nothing to take from left.
  147. inputBytes[inputByteIndex] = (byte)(inputBytes[inputByteIndex] + ((inputBytes[inputByteIndex - 1] & 0x1f) << 3)); //Take last 5 bits of next byte and shift to the left.
  148. }
  149. }
  150. return resultBytes;
  151. }
  152. private string ByteListToStringBinary(byte[] inputBytes)
  153. {
  154. var bitStream = ConvertBytesToBitStream(inputBytes);
  155. return BitStreamToString(bitStream);
  156. }
  157. private string BitStreamToString(bool[] bitStream)
  158. {
  159. var chars = new char[bitStream.Length];
  160. for (int i = 0; i < bitStream.Length; i++)
  161. {
  162. chars[i] = bitStream[i] ? '1' : '0';
  163. }
  164. return new string(chars);
  165. }
  166. private bool[] ConvertBytesToBitStream(byte[] inputBytes)
  167. {
  168. List<bool> bitStream = new List<bool>();
  169. foreach (var inputByte in inputBytes)
  170. {
  171. for (int i = 7; i >= 0; i--)
  172. {
  173. bitStream.Add((inputByte & (1 << i)) != 0);
  174. }
  175. }
  176. return bitStream.ToArray();
  177. }
  178. /// <summary>
  179. /// Returns the decoded bytes that was represented by the inputString.
  180. /// </summary>
  181. /// <param name="inputString"></param>
  182. /// <returns></returns>
  183. public byte[] ToBytes(string inputString)
  184. {
  185. byte[] inputBytes = Convert5BitStringToBytes(inputString);
  186. int numberOfResultingBytes = inputBytes.Length * 5 / 8;
  187. bool[] inputBitStream = new bool[inputBytes.Length * 5];
  188. for (int byteIndex = 0; byteIndex < inputBytes.Length; byteIndex++)
  189. {
  190. for (int bitIndex = 5; bitIndex >= 1; bitIndex--)
  191. inputBitStream[byteIndex * 5 + 5 - bitIndex] = IsBitSet(inputBytes[byteIndex], bitIndex);
  192. }
  193. //Console.WriteLine("BitStream 1: " + ByteListToStringBinary(inputBitStream));
  194. //Trim the bitstream to only contain the result bits.
  195. inputBitStream = TrimBitStream(inputBitStream, numberOfResultingBytes * 8);
  196. //Console.WriteLine("BitStream 2: " + ByteListToStringBinary(inputBitStream));
  197. byte[] outputBytes = new byte[numberOfResultingBytes];
  198. for (int byteIndex = 0; byteIndex < inputBitStream.Length / 8; byteIndex++) //Iterate the bytes within the bit array.
  199. {
  200. for (int bitIndex = 0; bitIndex < 8; bitIndex++) //Iterate through the bits within each byte.
  201. {
  202. bool b = inputBitStream[byteIndex * 8 + bitIndex];
  203. if (b)
  204. {
  205. outputBytes[byteIndex] = (byte)(outputBytes[byteIndex] | (1 << (7 - bitIndex)));
  206. }
  207. }
  208. }
  209. //Console.WriteLine("Output bytes: " + ByteListToStringBinary(outputBytes.ToArray()));
  210. return outputBytes;
  211. }
  212. private byte[] Convert5BitStringToBytes(string inputString)
  213. {
  214. byte[] result = new byte[inputString.Length];
  215. for (int i = 0; i < inputString.Length; i++)
  216. result[i] = ConvertChar(inputString[i]);
  217. return result;
  218. }
  219. private bool[] TrimBitStream(bool[] inputBitStream, int countToKeep)
  220. {
  221. bool[] newBitStream = new bool[countToKeep];
  222. Array.Copy(inputBitStream, inputBitStream.Length - countToKeep, newBitStream, 0, countToKeep);
  223. return newBitStream;
  224. }
  225. private bool IsBitSet(byte b, int byteNumber)
  226. {
  227. return (b & (1 << (byteNumber - 1))) != 0;
  228. }
  229. }
  230. }