|
|
Comments and Discussions
|
|
 |

|
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) :
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Base64
{
public abstract class Base64Encoding
{
private static char[] lookupTable = new char[64]
{ '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',
'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',
'0','1','2','3','4','5','6','7','8','9','+','/'};
private static char[] unvalidChars = new char[] { '\r', '\n', '\t' };
private static Dictionary<char, int> lookupTableDictionnary = new Dictionary<char, int>();
private static void BuidLookupTableDictionnary()
{
if(lookupTableDictionnary.Count > 0)
return;
for(int i = 0; i < lookupTable.Length; i++)
lookupTableDictionnary.Add(lookupTable[i], i);
}
private static char[] DeleteUnvalidChars(char[] input)
{
int numberOfValid = 0;
for(int i = 0; i < input.Length; i++)
{
if(Array.IndexOf(unvalidChars, input[i]) < 0)
numberOfValid++;
}
char[] validChars = new char[numberOfValid];
for(int i = 0, n = 0; i < input.Length; i++)
{
if(Array.IndexOf(unvalidChars, input[i]) < 0)
{
validChars[n] = input[i];
n++;
}
}
return validChars;
}
private static void DeleteUnvalidChars(FileStream inputStream, FileStream ouputStream)
{
byte[] b = new byte[1];
inputStream.Position = 0;
ouputStream.Position = 0;
while(inputStream.Read(b, 0, 1) > 0)
if(Array.IndexOf(unvalidChars, (char) b[0]) < 0)
ouputStream.Write(b, 0, b.Length);
}
private static byte CharToSixbit(char c)
{
if(c == '=')
return 0;
else
{
// Use the dictionnary for faster result !
if(Base64Encoding.lookupTableDictionnary.ContainsKey(c))
return (byte) Base64Encoding.lookupTableDictionnary[c];
//should not reach here
return 0;
}
}
private static char SixbitToChar(byte b)
{
if((b >= 0) && (b <= 63))
return lookupTable[(int) b];
else
return ' '; //should not happen;
}
public abstract class MemoryEncoding
{
/// <summary>
/// Summary description for Base64Encoder.
/// </summary>
public class MemoryBase64Encoder
{
byte[] source;
int length, length2;
int blockCount;
int paddingCount;
public MemoryBase64Encoder(string input)
{
byte[] inputBytes = System.Text.Encoding.Default.GetBytes(Base64Encoding.DeleteUnvalidChars(input.ToCharArray()));
this.InitEncoder(inputBytes);
}
public MemoryBase64Encoder(byte[] input)
{
this.InitEncoder(input);
}
private void InitEncoder(byte[] input)
{
Base64Encoding.BuidLookupTableDictionnary();
source = input;
length = input.Length;
if((length % 3) == 0)
{
paddingCount = 0;
blockCount = length / 3;
}
else
{
paddingCount = 3 - (length % 3);//need to add padding
blockCount = (length + paddingCount) / 3;
}
length2 = length + paddingCount;//or blockCount *3
}
public char[] Encode()
{
int i;
byte[] sourceBuffer;
if(length != length2)
{
sourceBuffer = new byte[length2];
//copy data over insert padding
for(i = 0; i < length2; i++)
{
if(i < length)
sourceBuffer[i] = source[i];
else
sourceBuffer[i] = 0;
}
}
else
{
sourceBuffer = source;
}
byte b1, b2, b3;
byte temp, temp1, temp2, temp3, temp4;
char[] result = new char[blockCount * 4];
for(i = 0; i < blockCount; i++)
{
b1 = sourceBuffer[i * 3];
b2 = sourceBuffer[i * 3 + 1];
b3 = sourceBuffer[i * 3 + 2];
temp1 = (byte) ((b1 & 252) >> 2);//first
temp = (byte) ((b1 & 3) << 4);
temp2 = (byte) ((b2 & 240) >> 4);
temp2 += temp; //second
temp = (byte) ((b2 & 15) << 2);
temp3 = (byte) ((b3 & 192) >> 6);
temp3 += temp; //third
temp4 = (byte) (b3 & 63); //fourth
result[i * 4] = Base64Encoding.SixbitToChar(temp1);
result[i * 4 + 1] = Base64Encoding.SixbitToChar(temp2);
result[i * 4 + 2] = Base64Encoding.SixbitToChar(temp3);
result[i * 4 + 3] = Base64Encoding.SixbitToChar(temp4);
}
//covert last "A"s to "=", based on paddingCount
switch(paddingCount)
{
case 0:
break;
case 1:
result[blockCount * 4 - 1] = '=';
break;
case 2:
result[blockCount * 4 - 1] = '=';
result[blockCount * 4 - 2] = '=';
break;
default:
break;
}
return result;
}
}
/// <summary>
/// Summary description for MemoryBase64Decoder.
/// </summary>
public class MemoryBase64Decoder
{
char[] source;
int length, length2, length3;
int blockCount;
int paddingCount;
public MemoryBase64Decoder(string input)
{
char[] inputChars = input.ToCharArray();
this.InitDecoder(inputChars);
}
public MemoryBase64Decoder(char[] input)
{
this.InitDecoder(input);
}
private void InitDecoder(char[] input)
{
Base64Encoding.BuidLookupTableDictionnary();
input = Base64Encoding.DeleteUnvalidChars(input);
int temp = 0;
source = input;
length = input.Length;
//find how many padding are there
for(int i = 0; i < 2; i++)
{
if(input[length - i - 1] == '=')
temp++;
}
paddingCount = temp;
//calculate the blockCount;
//assuming all whitespace and carriage returns/newline were removed.
blockCount = length / 4;
length2 = blockCount * 3;
}
public byte[] Decode()
{
byte[] buffer2 = new byte[length2];//decoded array with padding
// Conversion using the source as result
for(int i = 0; i < length; i++)
source[i] = (char) Base64Encoding.CharToSixbit(source[i]);
byte b, b1, b2, b3;
byte temp1, temp2, temp3, temp4;
for(int i = 0; i < blockCount; i++)
{
temp1 = (byte) source[i * 4];
temp2 = (byte) source[i * 4 + 1];
temp3 = (byte) source[i * 4 + 2];
temp4 = (byte) source[i * 4 + 3];
b = (byte) (temp1 << 2);
b1 = (byte) ((temp2 & 48) >> 4);
b1 += b;
b = (byte) ((temp2 & 15) << 4);
b2 = (byte) ((temp3 & 60) >> 2);
b2 += b;
b = (byte) ((temp3 & 3) << 6);
b3 = temp4;
b3 += b;
buffer2[i * 3] = b1;
buffer2[i * 3 + 1] = b2;
buffer2[i * 3 + 2] = b3;
}
//remove paddings
byte[] result = null;
if(paddingCount > 0)
{
length3 = length2 - paddingCount;
result = new byte[length3];
for(int i = 0; i < length3; i++)
result[i] = buffer2[i];
}
else
{
result = buffer2;
}
return result;
}
}
}
public abstract class FilesEncoding
{
/// <summary>
/// Summary description for FilesBase64Encoder.
/// </summary>
public class FilesBase64Encoder
{
private long sourceStreamInitialPosition = -1;
private long resultStreamInitialPosition = -1;
// Open files streams
private FileStream sourceStream = null; // source
private FileStream resultStream = null; // result
private string inputFilePath = string.Empty;
private string outputFilePath = string.Empty;
private string bufferFilePath = string.Empty;
long length, length2;
long blockCount;
long paddingCount;
public FilesBase64Encoder(FileStream inputStream, FileStream outputStream)
{
this.sourceStream = inputStream;
this.resultStream = outputStream;
this.bufferFilePath = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");
this.sourceStreamInitialPosition = sourceStream.Position;
this.resultStreamInitialPosition = resultStream.Position;
this.sourceStream.Position = 0;
this.resultStream.Position = 0;
this.InitEncoder();
}
public FilesBase64Encoder(string inputFilePath, string outputFilePath)
{
this.inputFilePath = inputFilePath;
this.outputFilePath = outputFilePath;
this.bufferFilePath = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");
this.InitEncoder();
}
private void InitEncoder()
{
Base64Encoding.BuidLookupTableDictionnary();
if(inputFilePath.Length > 0)
sourceStream = new FileStream(inputFilePath, FileMode.Open);
if(outputFilePath.Length > 0)
resultStream = new FileStream(outputFilePath, FileMode.Create);
length = sourceStream.Length;
if((length % 3) == 0)
{
paddingCount = 0;
blockCount = length / 3;
}
else
{
paddingCount = 3 - (length % 3);//need to add padding
blockCount = (length + paddingCount) / 3;
}
length2 = length + paddingCount;//or blockCount *3
}
public void Encode()
{
FileStream sourceBufferStream = null; // sourceBuffer
try
{
if(length != length2)
{
sourceBufferStream = new FileStream(bufferFilePath, FileMode.Create);
//copy data over insert padding
for(int i = 0; i < length2; i++)
{
if(i < length)
sourceBufferStream.WriteByte((byte) sourceStream.ReadByte());
else
sourceBufferStream.WriteByte((byte) 0);
}
}
else
{
sourceBufferStream = sourceStream;
}
sourceBufferStream.Position = 0;
byte[] threeBytes = new byte[3];
byte temp, temp1, temp2, temp3, temp4;
for(int i = 0; i < blockCount; i++)
{
sourceBufferStream.Read(threeBytes, 0, threeBytes.Length);
temp1 = (byte) ((threeBytes[0] & 252) >> 2);//first
temp = (byte) ((threeBytes[0] & 3) << 4);
temp2 = (byte) ((threeBytes[1] & 240) >> 4);
temp2 += temp; //second
temp = (byte) ((threeBytes[1] & 15) << 2);
temp3 = (byte) ((threeBytes[2] & 192) >> 6);
temp3 += temp; //third
temp4 = (byte) (threeBytes[2] & 63); //fourth
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp1));
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp2));
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp3));
resultStream.WriteByte((byte) Base64Encoding.SixbitToChar(temp4));
}
//covert last "A"s to "=", based on paddingCount
switch(paddingCount)
{
case 0:
break;
case 1:
resultStream.Position = resultStream.Length - 1;
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);
break;
case 2:
resultStream.Position = resultStream.Length - 1;
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);
resultStream.Position = resultStream.Length - 2;
resultStream.Write(new byte[] { (byte) '=' }, 0, 1);
break;
default:
break;
}
}
finally
{
this.sourceStreamInitialPosition = sourceStream.Position;
this.resultStreamInitialPosition = resultStream.Position;
if(inputFilePath.Length > 0)
{
if(sourceStream != null)
sourceStream.Close();
}
else
{
this.sourceStream.Position = this.sourceStreamInitialPosition;
}
if(outputFilePath.Length > 0)
{
if(resultStream != null)
resultStream.Close();
}
else
{
this.resultStream.Position = this.resultStreamInitialPosition;
}
if(sourceBufferStream != null)
if(sourceBufferStream != sourceStream)
sourceBufferStream.Close();
if(File.Exists(bufferFilePath))
File.Delete(bufferFilePath);
}
}
}
/// <summary>
/// Summary description for FilesBase64Decoder.
/// </summary>
public class FilesBase64Decoder
{
private long sourceStreamInitialPosition = -1;
private long resultStreamInitialPosition = -1;
private string inputFilePath = string.Empty;
private string outputFilePath = string.Empty;
private string bufferFilePath1 = string.Empty;
private string bufferFilePath2 = string.Empty;
private FileStream sourceStream = null; // source
private FileStream resultStream = null; // result
long length, length2, length3;
long blockCount;
long paddingCount;
public FilesBase64Decoder(string inputFilePath, string outputFilePath)
{
this.inputFilePath = inputFilePath;
this.outputFilePath = outputFilePath;
this.bufferFilePath1 = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");
this.bufferFilePath2 = Tools.GetUniqueTempFilePath("B64EncoderBuffer2", ".txt");
this.InitDecoder();
}
public FilesBase64Decoder(FileStream inputStream, FileStream outputStream)
{
this.sourceStream = inputStream;
this.resultStream = outputStream;
this.bufferFilePath1 = Tools.GetUniqueTempFilePath("B64EncoderBuffer1", ".txt");
this.bufferFilePath2 = Tools.GetUniqueTempFilePath("B64EncoderBuffer2", ".txt");
this.sourceStreamInitialPosition = this.sourceStream.Position;
this.resultStreamInitialPosition = this.resultStream.Position;
this.sourceStream.Position = 0;
this.resultStream.Position = 0;
this.InitDecoder();
}
private void InitDecoder()
{
Base64Encoding.BuidLookupTableDictionnary();
if(inputFilePath.Length > 0)
sourceStream = new FileStream(inputFilePath, FileMode.Open);
if(outputFilePath.Length > 0)
resultStream = new FileStream(outputFilePath, FileMode.Create);
FileStream sourceBufferStream = null;
try
{
sourceBufferStream = new FileStream(bufferFilePath1, FileMode.Create);
Base64Encoding.DeleteUnvalidChars(sourceStream, sourceBufferStream);
int temp = 0;
length = sourceBufferStream.Length;
byte[] tempByteArray = new byte[1];
//find how many padding are there
for(int i = 0; i < 2; i++)
{
sourceBufferStream.Position = length - i - 1;
sourceBufferStream.Read(tempByteArray, 0, 1);
if(tempByteArray[0] == '=')
temp++;
}
paddingCount = temp;
//calculate the blockCount;
//assuming all whitespace and carriage returns/newline were removed.
blockCount = length / 4;
length2 = blockCount * 3;
}
finally
{
if(sourceBufferStream != null)
sourceBufferStream.Close();
}
}
public void Decode()
{
FileStream sourceBufferStream1 = null;
FileStream sourceBufferStream2 = null;
try
{
sourceBufferStream1 = new FileStream(bufferFilePath1, FileMode.Open);
sourceBufferStream2 = new FileStream(bufferFilePath2, FileMode.Create);
byte[] buffer2 = new byte[length2];//decoded array with padding
byte[] tempByteArray = new byte[1];
sourceBufferStream1.Position = 0;
// Conversion using the source as result
for(int i = 0; i < length; i++)
{
sourceBufferStream1.Read(tempByteArray, 0, 1);
tempByteArray[0] = Base64Encoding.CharToSixbit((char) tempByteArray[0]);
sourceBufferStream2.Write(tempByteArray, 0, 1);
}
if(sourceBufferStream1 != null)
sourceBufferStream1.Close();
sourceBufferStream2.Position = 0;
sourceBufferStream1 = new FileStream(bufferFilePath1, FileMode.Create);
byte[] fourBytes = new byte[4];
byte b, b1, b2, b3;
byte temp1, temp2, temp3, temp4;
for(int i = 0; i < blockCount; i++)
{
sourceBufferStream2.Read(fourBytes, 0, fourBytes.Length);
temp1 = fourBytes[0];
temp2 = fourBytes[1];
temp3 = fourBytes[2];
temp4 = fourBytes[3];
b = (byte) (temp1 << 2);
b1 = (byte) ((temp2 & 48) >> 4);
b1 += b;
b = (byte) ((temp2 & 15) << 4);
b2 = (byte) ((temp3 & 60) >> 2);
b2 += b;
b = (byte) ((temp3 & 3) << 6);
b3 = temp4;
b3 += b;
sourceBufferStream1.WriteByte(b1);
sourceBufferStream1.WriteByte(b2);
sourceBufferStream1.WriteByte(b3);
}
sourceBufferStream1.Position = 0;
//remove paddings
if(paddingCount > 0)
{
length3 = length2 - paddingCount;
for(int i = 0; i < length3; i++)
resultStream.WriteByte((byte) sourceBufferStream1.ReadByte());
}
else
{
for(int i = 0; i < length2; i++)
resultStream.WriteByte((byte) sourceBufferStream1.ReadByte());
}
}
finally
{
if(inputFilePath.Length > 0)
{
if(sourceStream != null)
sourceStream.Close();
}
else
{
this.sourceStream.Position = this.sourceStreamInitialPosition;
}
if(outputFilePath.Length > 0)
{
if(resultStream != null)
resultStream.Close();
}
else
{
this.resultStream.Position = this.resultStreamInitialPosition;
}
if(sourceBufferStream1 != null)
sourceBufferStream1.Close();
if(sourceBufferStream2 != null)
sourceBufferStream2.Close();
if(File.Exists(bufferFilePath1))
File.Delete(bufferFilePath1);
if(File.Exists(bufferFilePath2))
File.Delete(bufferFilePath2);
}
}
}
}
}
public abstract class Tools
{
public static string GetUniqueTempFilePath(string beginWith, string fileExtension)
{
// Attendre une milliseconde pour être sûrs de l'unicité de nom de fichier
System.Threading.Thread.Sleep(1);
return Path.Combine(Path.GetTempPath(), GetUniqueTempFileName(beginWith, fileExtension));
}
public static string GetUniqueTempFileName(string beginWith, string fileExtension)
{
// Attendre une milliseconde pour être sûrs de l'unicité de nom de fichier
System.Threading.Thread.Sleep(1);
return beginWith + " " + GetDateString(DateTime.Now) + fileExtension;
}
public static string GetDateString(DateTime d)
{
return d.Year.ToString() + "_"
+ d.Month.ToString() + "_"
+ d.Day.ToString() + "_"
+ d.Hour.ToString() + "_"
+ d.Minute.ToString() + "_"
+ d.Second.ToString() + "_"
+ d.Millisecond.ToString();
}
}
}
I hope this helps people having the same issue.
Best regards.
joujoukinder
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
An implementation of Base64 encoding/decoding in C#
| Type | Article |
| Licence | |
| First Posted | 19 Nov 2003 |
| Views | 221,953 |
| Bookmarked | 58 times |
|
|