Hello,
Very nice your encoder !
The only problem is that it works on RAM only, and on x86 computers, when processing large data, the process needs to much memory and fires a System.OutOfMemoryException.
The solution is to use file streams instead of the RAM. It will be less quick, but will work for large data.
Actually, I use the encoder to parse email files. When an email has an attachment that is bigger than 30 MB, the encoder crashes.
I wrote my own class, based on yours, that includes files streams.
If you like, you can use / modify it to update this article (The class design is not the best.. ).
Here is the code (partially commented) :
<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Text;<br />
using System.IO;<br />
<br />
namespace Base64<br />
{<br />
public abstract class Base64Encoding<br />
{<br />
private static char[] lookupTable = new char[64]<br />
{ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',<br />
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',<br />
'0','1','2','3','4','5','6','7','8','9','+','/'};<br />
private static char[] unvalidChars = new char[] { '\r', '\n', '\t' };<br />
private static Dictionary<char, int> lookupTableDictionnary = new Dictionary<char, int>();<br />
<br />
private static void BuidLookupTableDictionnary()<br />
{<br />
if(lookupTableDictionnary.Count > 0)<br />
return;<br />
<br />
for(int i = 0; i < lookupTable.Length; i++)<br />
lookupTableDictionnary.Add(lookupTable[i], i);<br />
}<br />
private static char[] DeleteUnvalidChars(char[] input)<br />
{<br />
int numberOfValid = 0;<br />
<br />
for(int i = 0; i < input.Length; i++)<br />
{<br />
if(Array.IndexOf(unvalidChars, input[i]) < 0)<br />
numberOfValid++;<br />
}<br />
char[] validChars = new char[numberOfValid];<br />
<br />
for(int i = 0, n = 0; i < input.Length; i++)<br />
{<br />
if(Array.IndexOf(unvalidChars, input[i]) < 0)<br />
{<br />
validChars[n] = input[i];<br />
n++;<br />
}<br />
}<br />
<br />
return validChars;<br />
}<br />
<br />
private static void DeleteUnvalidChars(FileStream inputStream, FileStream ouputStream)<br />
{<br />
byte[] b = new byte[1];<br />
<br />
inputStream.Position = 0;<br />
ouputStream.Position = 0;<br />
<br />
while(inputStream.Read(b, 0, 1) > 0)<br />
if(Array.IndexOf(unvalidChars, (char) b[0]) < 0)<br />
ouputStream.Write(b, 0, b.Length);<br />
}<br />
<br />
private static byte CharToSixbit(char c)<br />
{<br />
if(c == '=')<br />
return 0;<br />
<br />
else<br />
{<br />
if(Base64Encoding.lookupTableDictionnary.ContainsKey(c))<br />
return (byte) Base64Encoding.lookupTableDictionnary[c];<br />
<br />
return 0;<br />
}<br />
}<br />
private static char SixbitToChar(byte b)<br />
{<br />
if((b >= 0) && (b <= 63))<br />
return lookupTable[(int) b];<br />
else<br />
return ' ';
}<br />
<br />
public abstract class MemoryEncoding<br />
{<br />
public class MemoryBase64Encoder<br />
{<br />
byte[] source;<br />
int length, length2;<br />
int blockCount;<br />
int paddingCount;<br />
<br />
public MemoryBase64Encoder(string input)<br />
{<br />
byte[] inputBytes = System.Text.Encoding.Default.GetBytes(Base64Encoding.DeleteUnvalidChars(input.ToCharArray()));<br />
this.InitEncoder(inputBytes);<br />
}<br />
<br />
public MemoryBase64Encoder(byte[] input)<br />
{<br />
this.InitEncoder(input);<br />
}<br />
<br />
private void InitEncoder(byte[] input)<br />
{<br />
Base64Encoding.BuidLookupTableDictionnary();<br />
<br />
source = input;<br />
length = input.Length;<br />
<br />
if((length % 3) == 0)<br />
{<br />
paddingCount = 0;<br />
blockCount = length / 3;<br />
}<br />
else<br />
{<br />
paddingCount = 3 - (length % 3);
blockCount = (length + paddingCount) / 3;<br />
}<br />
length2 = length + paddingCount;
}<br />
<br />
public char[] Encode()<br />
{<br />
int i;<br />
<br />
byte[] sourceBuffer;<br />
<br />
if(length != length2)<br />
{<br />
sourceBuffer = new byte[length2];<br />
<br />
for(i = 0; i < length2; i++)<br />
{<br />
if(i < length)<br />
sourceBuffer[i] = source[i];<br />
else<br />
sourceBuffer[i] = 0;<br />
}<br />
}<br />
else<br />
{<br />
sourceBuffer = source;<br />
}<br />
<br />
byte b1, b2, b3;<br />
byte temp, temp1, temp2, temp3, temp4;<br />
char[] result = new char[blockCount * 4];<br />
<br />
for(i = 0; i < blockCount; i++)<br />
{<br />
b1 = sourceBuffer[i * 3];<br />
b2 = sourceBuffer[i * 3 + 1];<br />
b3 = sourceBuffer[i * 3 + 2];<br />
<br />
temp1 = (byte) ((b1 & 252) >> 2);
<br />
temp = (byte) ((b1 & 3) << 4);<br />
temp2 = (byte) ((b2 & 240) >> 4);<br />
temp2 += temp;
<br />
temp = (byte) ((b2 & 15) << 2);<br />
temp3 = (byte) ((b3 & 192) >> 6);<br />
temp3 += temp;
<br />
temp4 = (byte) (b3 & 63);
<br />
result[i * 4] = Base64Encoding.SixbitToChar(temp1);<br />
result[i * 4 + 1] = Base64Encoding.SixbitToChar(temp2);<br />
result[i * 4 + 2] = Base64Encoding.SixbitToChar(temp3);<br />
result[i * 4 + 3] = Base64Encoding.SixbitToChar(temp4);<br />
}<br />
<br />
switch(paddingCount)<br />
{<br />
case 0:<br />
break;<br />
<br />
case 1:<br />
<br />
result[blockCount * 4 - 1] = '=';<br />
<br />
break;<br />
<br />
case 2:<br />
<br />
result[blockCount * 4 - 1] = '=';<br />
result[blockCount * 4 - 2] = '=';<br />
<br />
break;<br />
<br />
default:<br />
break;<br />
}<br />
return result;<br />
}<br />
}<br />
<br />
public class MemoryBase64Decoder<br />
{<br />
char[] source;<br />
int length, length2, length3;<br />
int blockCount;<br />
int paddingCount;<br />
<br />
public MemoryBase64Decoder(string input)<br />
{<br />
char[] inputChars = input.ToCharArray();<br />
this.InitDecoder(inputChars);<br />
}<br />
<br />
public MemoryBase64Decoder(char[] input)<br />
{<br />
this.InitDecoder(input);<br />
}<br />
<br />
private void InitDecoder(char[] input)<br />
{<br />
Base64Encoding.BuidLookupTableDictionnary();<br />
<br />
input = Base64Encoding.DeleteUnvalidChars(input);<br />
<br />
int temp = 0;<br />
source = input;<br />
length = input.Length;<br />
<br />
for(int i = 0; i < 2; i++)<br />
{<br />
if(input[length - i - 1] == '=')<br />
temp++;<br />
}<br />
paddingCount = temp;<br />
blockCount = length / 4;<br />
length2 = blockCount * 3;<br />
}<br />
<br />
public byte[] Decode()<br />
{<br />
byte[] buffer2 = new byte[length2];
<br />
for(int i = 0; i < length; i++)<br />
source[i] = (char) Base64Encoding.CharToSixbit(source[i]);<br />
<br />
byte b, b1, b2, b3;<br />
byte temp1, temp2, temp3, temp4;<br />
<br />
for(int i = 0; i < blockCount; i++)<br />
{<br />
temp1 = (byte) source[i * 4];<br />
temp2 = (byte) source[i * 4 + 1];<br />
temp3 = (byte) source[i * 4 + 2];<br />
temp4 = (byte) source[i * 4 + 3];<br />
<br />
b = (byte) (temp1 << 2);<br />
b1 = (byte) ((temp2 & 48) >> 4);<br />
b1 += b;<br />
<br />
b = (byte) ((temp2 & 15) << 4);<br />
b2 = (byte) ((temp3 & 60) >> 2);<br />
b2 += b;<br />
<br />
b = (byte) ((temp3 & 3) << 6);<br />
b3 = temp4;<br />
b3 += b;<br />
<br />
buffer2[i * 3] = b1;<br />
buffer2[i * 3 + 1] = b2;<br />
buffer2[i * 3 + 2] = b3;<br />
}<br />
<br />
byte[] result = null;<br />
<br />
if(paddingCount > 0)<br />
{<br />
length3 = length2 - paddingCount;<br />
result = new byte[length3];<br />
<br />
for(int i = 0; i < length3; i++)<br />
result[i] = buffer2[i];<br />
}<br />
else<br />
{<br />
result = buffer2;<br />
}<br />
<br />
return result;<br />
}<br />
}<br />
}<br />
public abstract class FilesEncoding<br />
{<br />
public class FilesBase64Encoder<br />
{<br />
private long sourceStreamInitialPosition = -1;<br />
private long resultStreamInitialPosition = -1;<br />
<br />
private FileStream sourceStream = null;
private FileStream resultStream = null;
<br />
private string inputFilePath = string.Empty;<br />
private string outputFilePath = string.Empty;<br />
private string bufferFilePath = string.Empty;<br />
<br />
long length, length2;<br />
long blockCount;<br />
long paddingCount;<br />
<br />
public FilesBase64Encoder(FileStream inputStream, FileStream outputStream)<br />
{<br />
this.sourceStream = inputStream;<br />
this.resultStream = outputStream;<br />
this.bufferFilePath = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");<br />
<br />
this.sourceStreamInitialPosition = sourceStream.Position;<br />
this.resultStreamInitialPosition = resultStream.Position;<br />
<br />
this.sourceStream.Position = 0;<br />
this.resultStream.Position = 0;<br />
<br />
this.InitEncoder();<br />
}<br />
<br />
public FilesBase64Encoder(string inputFilePath, string outputFilePath)<br />
{<br />
this.inputFilePath = inputFilePath;<br />
this.outputFilePath = outputFilePath;<br />
this.bufferFilePath = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");<br />
<br />
this.InitEncoder();<br />
}<br />
<br />
<br />
private void InitEncoder()<br />
{<br />
Base64Encoding.BuidLookupTableDictionnary();<br />
<br />
if(inputFilePath.Length > 0)<br />
sourceStream = new FileStream(inputFilePath, FileMode.Open);<br />
<br />
if(outputFilePath.Length > 0)<br />
resultStream = new FileStream(outputFilePath, FileMode.Create);<br />
<br />
length = sourceStream.Length;<br />
<br />
if((length % 3) == 0)<br />
{<br />
paddingCount = 0;<br />
blockCount = length / 3;<br />
}<br />
else<br />
{<br />
paddingCount = 3 - (length % 3);
blockCount = (length + paddingCount) / 3;<br />
}<br />
<br />
length2 = length + paddingCount;
}<br />
<br />
public void Encode()<br />
{<br />
FileStream sourceBufferStream = null;
<br />
try<br />
{<br />
if(length != length2)<br />
{<br />
sourceBufferStream = new FileStream(bufferFilePath, FileMode.Create);<br />
<br />
for(int i = 0; i < length2; i++)<br />
{<br />
if(i < length)<br />
sourceBufferStream.WriteByte((byte) sourceStream.ReadByte());<br />
else<br />
sourceBufferStream.WriteByte((byte) 0);<br />
}<br />
}<br />
else<br />
{<br />
sourceBufferStream = sourceStream;<br />
}<br />
<br />
sourceBufferStream.Position = 0;<br />
<br />
byte[] threeBytes = new byte[3];<br />
byte temp, temp1, temp2, temp3, temp4;<br />
for(int i = 0; i < blockCount; i++)<br />
{<br />
sourceBufferStream.Read(threeBytes, 0, threeBytes.Length);<br />
<br />
temp1 = (byte) ((threeBytes[0] & 252) >> 2);
<br />
temp = (byte) ((threeBytes[0] & 3) << 4);<br />
temp2 = (byte) ((threeBytes[1] & 240) >> 4);<br />
temp2 += temp;
<br />
temp = (byte) ((threeBytes[1] & 15) << 2);<br />
temp3 = (byte) ((threeBytes[2] & 192) >> 6);<br />
temp3 += temp;
<br />
temp4 = (byte) (threeBytes[2] & 63);
<br />
<br />
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp1));<br />
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp2));<br />
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp3));<br />
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp4));<br />
}<br />
<br />
switch(paddingCount)<br />
{<br />
case 0:<br />
break;<br />
<br />
case 1:<br />
<br />
resultStream.Position = resultStream.Length - 1;<br />
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);<br />
<br />
break;<br />
<br />
case 2:<br />
<br />
resultStream.Position = resultStream.Length - 1;<br />
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);<br />
<br />
resultStream.Position = resultStream.Length - 2;<br />
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);<br />
<br />
break;<br />
<br />
default:<br />
break;<br />
}<br />
}<br />
finally<br />
{<br />
this.sourceStreamInitialPosition = sourceStream.Position;<br />
this.resultStreamInitialPosition = resultStream.Position;<br />
<br />
if(inputFilePath.Length > 0)<br />
{<br />
if(sourceStream != null)<br />
sourceStream.Close();<br />
}<br />
else<br />
{<br />
this.sourceStream.Position = this.sourceStreamInitialPosition;<br />
}<br />
<br />
if(outputFilePath.Length > 0)<br />
{<br />
if(resultStream != null)<br />
resultStream.Close();<br />
}<br />
else<br />
{<br />
this.resultStream.Position = this.resultStreamInitialPosition;<br />
}<br />
<br />
if(sourceBufferStream != null)<br />
if(sourceBufferStream != sourceStream)<br />
sourceBufferStream.Close();<br />
<br />
if(File.Exists(bufferFilePath))<br />
File.Delete(bufferFilePath);<br />
}<br />
}<br />
}<br />
<br />
public class FilesBase64Decoder<br />
{<br />
private long sourceStreamInitialPosition = -1;<br />
private long resultStreamInitialPosition = -1;<br />
<br />
private string inputFilePath = string.Empty;<br />
private string outputFilePath = string.Empty;<br />
private string bufferFilePath1 = string.Empty;<br />
private string bufferFilePath2 = string.Empty;<br />
<br />
private FileStream sourceStream = null;
private FileStream resultStream = null;
<br />
long length, length2, length3;<br />
long blockCount;<br />
long paddingCount;<br />
<br />
public FilesBase64Decoder(string inputFilePath, string outputFilePath)<br />
{<br />
this.inputFilePath = inputFilePath;<br />
this.outputFilePath = outputFilePath;<br />
this.bufferFilePath1 = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");<br />
this.bufferFilePath2 = Tools.GetUniqueTempFilePath("B64EncoderBuffer2", ".txt");<br />
<br />
this.InitDecoder();<br />
}<br />
<br />
public FilesBase64Decoder(FileStream inputStream, FileStream outputStream)<br />
{<br />
this.sourceStream = inputStream;<br />
this.resultStream = outputStream;<br />
this.bufferFilePath1 = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");<br />
this.bufferFilePath2 = Tools.GetUniqueTempFilePath("B64EncoderBuffer2", ".txt");<br />
<br />
this.sourceStreamInitialPosition = this.sourceStream.Position;<br />
this.resultStreamInitialPosition = this.resultStream.Position;<br />
<br />
this.sourceStream.Position = 0;<br />
this.resultStream.Position = 0;<br />
<br />
this.InitDecoder();<br />
}<br />
<br />
private void InitDecoder()<br />
{<br />
Base64Encoding.BuidLookupTableDictionnary();<br />
<br />
if(inputFilePath.Length > 0)<br />
sourceStream = new FileStream(inputFilePath, FileMode.Open);<br />
<br />
if(outputFilePath.Length > 0)<br />
resultStream = new FileStream(outputFilePath, FileMode.Create);<br />
<br />
FileStream sourceBufferStream = null;<br />
<br />
try<br />
{<br />
sourceBufferStream = new FileStream(bufferFilePath1, FileMode.Create);<br />
Base64Encoding.DeleteUnvalidChars(sourceStream, sourceBufferStream);<br />
<br />
int temp = 0;<br />
length = sourceBufferStream.Length;<br />
<br />
byte[] tempByteArray = new byte[1];<br />
<br />
for(int i = 0; i < 2; i++)<br />
{<br />
sourceBufferStream.Position = length - i - 1;<br />
sourceBufferStream.Read(tempByteArray, 0, 1);<br />
<br />
if(tempByteArray[0] == '=')<br />
temp++;<br />
}<br />
<br />
paddingCount = temp;<br />
blockCount = length / 4;<br />
length2 = blockCount * 3;<br />
}<br />
finally<br />
{<br />
if(sourceBufferStream != null)<br />
sourceBufferStream.Close();<br />
}<br />
}<br />
<br />
public void Decode()<br />
{<br />
FileStream sourceBufferStream1 = null;<br />
FileStream sourceBufferStream2 = null;<br />
<br />
try<br />
{<br />
sourceBufferStream1 = new FileStream(bufferFilePath1, FileMode.Open);<br />
sourceBufferStream2 = new FileStream(bufferFilePath2, FileMode.Create);<br />
<br />
byte[] buffer2 = new byte[length2];
byte[] tempByteArray = new byte[1];<br />
<br />
sourceBufferStream1.Position = 0;<br />
<br />
for(int i = 0; i < length; i++)<br />
{<br />
sourceBufferStream1.Read(tempByteArray, 0, 1);<br />
tempByteArray[0] = Base64Encoding.CharToSixbit((char) tempByteArray[0]);<br />
sourceBufferStream2.Write(tempByteArray, 0, 1);<br />
}<br />
<br />
if(sourceBufferStream1 != null)<br />
sourceBufferStream1.Close();<br />
<br />
sourceBufferStream2.Position = 0;<br />
<br />
sourceBufferStream1 = new FileStream(bufferFilePath1, FileMode.Create);<br />
<br />
byte[] fourBytes = new byte[4];<br />
<br />
byte b, b1, b2, b3;<br />
byte temp1, temp2, temp3, temp4;<br />
<br />
for(int i = 0; i < blockCount; i++)<br />
{<br />
sourceBufferStream2.Read(fourBytes, 0, fourBytes.Length);<br />
<br />
temp1 = fourBytes[0];<br />
temp2 = fourBytes[1];<br />
temp3 = fourBytes[2];<br />
temp4 = fourBytes[3];<br />
<br />
b = (byte) (temp1 << 2);<br />
b1 = (byte) ((temp2 & 48) >> 4);<br />
b1 += b;<br />
<br />
b = (byte) ((temp2 & 15) << 4);<br />
b2 = (byte) ((temp3 & 60) >> 2);<br />
b2 += b;<br />
<br />
b = (byte) ((temp3 & 3) << 6);<br />
b3 = temp4;<br />
b3 += b;<br />
<br />
sourceBufferStream1.WriteByte(b1);<br />
sourceBufferStream1.WriteByte(b2);<br />
sourceBufferStream1.WriteByte(b3);<br />
}<br />
<br />
sourceBufferStream1.Position = 0;<br />
<br />
if(paddingCount > 0)<br />
{<br />
length3 = length2 - paddingCount;<br />
<br />
for(int i = 0; i < length3; i++)<br />
resultStream.WriteByte((byte) sourceBufferStream1.ReadByte());<br />
}<br />
else<br />
{<br />
for(int i = 0; i < length2; i++)<br />
resultStream.WriteByte((byte) sourceBufferStream1.ReadByte());<br />
}<br />
}<br />
finally<br />
{<br />
if(inputFilePath.Length > 0)<br />
{<br />
if(sourceStream != null)<br />
sourceStream.Close();<br />
}<br />
else<br />
{<br />
this.sourceStream.Position = this.sourceStreamInitialPosition;<br />
}<br />
if(outputFilePath.Length > 0)<br />
{<br />
if(resultStream != null)<br />
resultStream.Close();<br />
}<br />
else<br />
{<br />
this.resultStream.Position = this.resultStreamInitialPosition;<br />
}<br />
<br />
if(sourceBufferStream1 != null)<br />
sourceBufferStream1.Close();<br />
<br />
if(sourceBufferStream2 != null)<br />
sourceBufferStream2.Close();<br />
<br />
if(File.Exists(bufferFilePath1))<br />
File.Delete(bufferFilePath1);<br />
<br />
if(File.Exists(bufferFilePath2))<br />
File.Delete(bufferFilePath2);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
public abstract class Tools<br />
{<br />
public static string GetUniqueTempFilePath(string beginWith, string fileExtension)<br />
{<br />
System.Threading.Thread.Sleep(1);<br />
return Path.Combine(Path.GetTempPath(), GetUniqueTempFileName(beginWith, fileExtension));<br />
}<br />
<br />
public static string GetUniqueTempFileName(string beginWith, string fileExtension)<br />
{<br />
System.Threading.Thread.Sleep(1);<br />
<br />
return beginWith + " " + GetDateString(DateTime.Now) + fileExtension;<br />
}<br />
<br />
public static string GetDateString(DateTime d)<br />
{<br />
return d.Year.ToString() + "_"<br />
+ d.Month.ToString() + "_"<br />
+ d.Day.ToString() + "_"<br />
+ d.Hour.ToString() + "_"<br />
+ d.Minute.ToString() + "_"<br />
+ d.Second.ToString() + "_"<br />
+ d.Millisecond.ToString();<br />
<br />
}<br />
}<br />
}<br />
<br />
<br />
I hope this helps people having the same issue.
Best regards.
joujoukinder
|