using System; using System.Collections.Generic; using System.Text; namespace Wayne.Lib { /// /// Binary Encoder/Decoder with the custom 5-bit-per-byte format that /// is especially good for screen viewing, since the characters that /// may be confused has been eliminated (I/1/l/O/0). The purpose is /// to encode binary data into a string representation. /// public class BinaryConvert5Bit { /// /// Vadildates if a given set of characters are valid. /// /// /// public bool Valid5BitString(IEnumerable s) { const string allowedChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; foreach (char character in s) { if (allowedChars.IndexOf(character) < 0) return false; } return true; } /// /// Returns the string representation of the bytes /// /// /// public string ToString(byte[] bytes) { byte[] fivebitBytes = EncodeFiveBitPerBytesArray(bytes); string encodedString = ConvertToString(fivebitBytes); return encodedString; } private string ConvertToString(byte[] bytes) { StringBuilder sb = new StringBuilder(); foreach (var b in bytes) { sb.Append(ConvertByte(b)); } return sb.ToString(); } private char ConvertByte(byte b) { switch (b) { case 0: return '2'; case 1: return '3'; case 2: return '4'; case 3: return '5'; case 4: return '6'; case 5: return '7'; case 6: return '8'; case 7: return '9'; case 8: return 'A'; case 9: return 'B'; case 10: return 'C'; case 11: return 'D'; case 12: return 'E'; case 13: return 'F'; case 14: return 'G'; case 15: return 'H'; case 16: return 'J'; case 17: return 'K'; case 18: return 'L'; case 19: return 'M'; case 20: return 'N'; case 21: return 'P'; case 22: return 'Q'; case 23: return 'R'; case 24: return 'S'; case 25: return 'T'; case 26: return 'U'; case 27: return 'V'; case 28: return 'W'; case 29: return 'X'; case 30: return 'Y'; case 31: return 'Z'; } throw new ArgumentException(string.Format("Invalid byte {0} it contains more than 5 bits.", b)); } private byte ConvertChar(char c) { switch (c) { case '2': return 0; case '3': return 1; case '4': return 2; case '5': return 3; case '6': return 4; case '7': return 5; case '8': return 6; case '9': return 7; case 'A': return 8; case 'B': return 9; case 'C': return 10; case 'D': return 11; case 'E': return 12; case 'F': return 13; case 'G': return 14; case 'H': return 15; case 'J': return 16; case 'K': return 17; case 'L': return 18; case 'M': return 19; case 'N': return 20; case 'P': return 21; case 'Q': return 22; case 'R': return 23; case 'S': return 24; case 'T': return 25; case 'U': return 26; case 'V': return 27; case 'W': return 28; case 'X': return 29; case 'Y': return 30; case 'Z': return 31; } throw new ArgumentException(string.Format("Invalid char {0}.", c)); } private byte[] EncodeFiveBitPerBytesArray(byte[] bytes) { byte[] inputBytes = (byte[])bytes.Clone(); //Calculate resulting number of bytes int inputBitCount = inputBytes.Length * 8; int resultingBytesCount = inputBitCount / 5; if (inputBitCount % 5 > 0) resultingBytesCount++; byte[] resultBytes = new byte[resultingBytesCount]; for (int resultByteIndex = resultingBytesCount - 1; resultByteIndex >= 0; resultByteIndex--) { // Console.WriteLine(ByteListToStringBinary(inputBytes)); //Get the last five bits of the last byte in the byte array. byte fiveBits = (byte)(inputBytes[inputBytes.Length - 1] & 0x1f); resultBytes[resultByteIndex] = fiveBits; //Shift all the bits five places to the right for (int inputByteIndex = inputBytes.Length - 1; inputByteIndex >= 0; inputByteIndex--) { inputBytes[inputByteIndex] = (byte)(inputBytes[inputByteIndex] >> 5);//Shift the remaining 3 bits to the right if (inputByteIndex > 0)//If we are at the leftmost byte, there is nothing to take from left. inputBytes[inputByteIndex] = (byte)(inputBytes[inputByteIndex] + ((inputBytes[inputByteIndex - 1] & 0x1f) << 3)); //Take last 5 bits of next byte and shift to the left. } } return resultBytes; } private string ByteListToStringBinary(byte[] inputBytes) { var bitStream = ConvertBytesToBitStream(inputBytes); return BitStreamToString(bitStream); } private string BitStreamToString(bool[] bitStream) { var chars = new char[bitStream.Length]; for (int i = 0; i < bitStream.Length; i++) { chars[i] = bitStream[i] ? '1' : '0'; } return new string(chars); } private bool[] ConvertBytesToBitStream(byte[] inputBytes) { List bitStream = new List(); foreach (var inputByte in inputBytes) { for (int i = 7; i >= 0; i--) { bitStream.Add((inputByte & (1 << i)) != 0); } } return bitStream.ToArray(); } /// /// Returns the decoded bytes that was represented by the inputString. /// /// /// public byte[] ToBytes(string inputString) { byte[] inputBytes = Convert5BitStringToBytes(inputString); int numberOfResultingBytes = inputBytes.Length * 5 / 8; bool[] inputBitStream = new bool[inputBytes.Length * 5]; for (int byteIndex = 0; byteIndex < inputBytes.Length; byteIndex++) { for (int bitIndex = 5; bitIndex >= 1; bitIndex--) inputBitStream[byteIndex * 5 + 5 - bitIndex] = IsBitSet(inputBytes[byteIndex], bitIndex); } //Console.WriteLine("BitStream 1: " + ByteListToStringBinary(inputBitStream)); //Trim the bitstream to only contain the result bits. inputBitStream = TrimBitStream(inputBitStream, numberOfResultingBytes * 8); //Console.WriteLine("BitStream 2: " + ByteListToStringBinary(inputBitStream)); byte[] outputBytes = new byte[numberOfResultingBytes]; for (int byteIndex = 0; byteIndex < inputBitStream.Length / 8; byteIndex++) //Iterate the bytes within the bit array. { for (int bitIndex = 0; bitIndex < 8; bitIndex++) //Iterate through the bits within each byte. { bool b = inputBitStream[byteIndex * 8 + bitIndex]; if (b) { outputBytes[byteIndex] = (byte)(outputBytes[byteIndex] | (1 << (7 - bitIndex))); } } } //Console.WriteLine("Output bytes: " + ByteListToStringBinary(outputBytes.ToArray())); return outputBytes; } private byte[] Convert5BitStringToBytes(string inputString) { byte[] result = new byte[inputString.Length]; for (int i = 0; i < inputString.Length; i++) result[i] = ConvertChar(inputString[i]); return result; } private bool[] TrimBitStream(bool[] inputBitStream, int countToKeep) { bool[] newBitStream = new bool[countToKeep]; Array.Copy(inputBitStream, inputBitStream.Length - countToKeep, newBitStream, 0, countToKeep); return newBitStream; } private bool IsBitSet(byte b, int byteNumber) { return (b & (1 << (byteNumber - 1))) != 0; } } }