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;
}
}
}