171 lines
4.3 KiB
C#
171 lines
4.3 KiB
C#
using System;
|
|
using System.Text;
|
|
|
|
namespace BaseNcoding
|
|
{
|
|
public class Base64 : Base
|
|
{
|
|
public const string DefaultAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
public const char DefaultSpecial = '=';
|
|
|
|
public override bool HasSpecial => true;
|
|
|
|
public Base64(string alphabet = DefaultAlphabet, char special = DefaultSpecial,
|
|
Encoding textEncoding = null, bool parallel = false)
|
|
: base(64, alphabet, special, textEncoding, parallel)
|
|
{
|
|
}
|
|
|
|
public override string Encode(byte[] data)
|
|
{
|
|
int resultLength = (data.Length + 2) / 3 * 4;
|
|
char[] result = new char[resultLength];
|
|
|
|
int length3 = data.Length / 3;
|
|
if (!Parallel)
|
|
{
|
|
EncodeBlock(data, result, 0, length3);
|
|
}
|
|
else
|
|
{
|
|
int processorCount = Math.Min(length3, Environment.ProcessorCount);
|
|
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
|
{
|
|
int beginInd = i * length3 / processorCount;
|
|
int endInd = (i + 1) * length3 / processorCount;
|
|
EncodeBlock(data, result, beginInd, endInd);
|
|
});
|
|
}
|
|
|
|
int ind;
|
|
int x1, x2;
|
|
int srcInd, dstInd;
|
|
switch (data.Length - length3 * 3)
|
|
{
|
|
case 1:
|
|
ind = length3;
|
|
srcInd = ind * 3;
|
|
dstInd = ind * 4;
|
|
x1 = data[srcInd];
|
|
result[dstInd] = Alphabet[x1 >> 2];
|
|
result[dstInd + 1] = Alphabet[(x1 << 4) & 0x30];
|
|
result[dstInd + 2] = Special;
|
|
result[dstInd + 3] = Special;
|
|
break;
|
|
case 2:
|
|
ind = length3;
|
|
srcInd = ind * 3;
|
|
dstInd = ind * 4;
|
|
x1 = data[srcInd];
|
|
x2 = data[srcInd + 1];
|
|
result[dstInd] = Alphabet[x1 >> 2];
|
|
result[dstInd + 1] = Alphabet[((x1 << 4) & 0x30) | (x2 >> 4)];
|
|
result[dstInd + 2] = Alphabet[(x2 << 2) & 0x3C];
|
|
result[dstInd + 3] = Special;
|
|
break;
|
|
}
|
|
|
|
return new string(result);
|
|
}
|
|
|
|
public override byte[] Decode(string data)
|
|
{
|
|
unchecked
|
|
{
|
|
if (string.IsNullOrEmpty(data))
|
|
return new byte[0];
|
|
|
|
int lastSpecialInd = data.Length;
|
|
while (data[lastSpecialInd - 1] == Special)
|
|
lastSpecialInd--;
|
|
int tailLength = data.Length - lastSpecialInd;
|
|
|
|
int resultLength = (data.Length + 3) / 4 * 3 - tailLength;
|
|
byte[] result = new byte[resultLength];
|
|
|
|
int length4 = (data.Length - tailLength) / 4;
|
|
if (!Parallel)
|
|
{
|
|
DecodeBlock(data, result, 0, length4);
|
|
}
|
|
else
|
|
{
|
|
int processorCount = Math.Min(length4, Environment.ProcessorCount);
|
|
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
|
{
|
|
int beginInd = i * length4 / processorCount;
|
|
int endInd = (i + 1) * length4 / processorCount;
|
|
DecodeBlock(data, result, beginInd, endInd);
|
|
});
|
|
}
|
|
|
|
int ind;
|
|
int x1, x2, x3;
|
|
int srcInd, dstInd;
|
|
switch (tailLength)
|
|
{
|
|
case 2:
|
|
ind = length4;
|
|
srcInd = ind * 4;
|
|
dstInd = ind * 3;
|
|
x1 = InvAlphabet[data[srcInd]];
|
|
x2 = InvAlphabet[data[srcInd + 1]];
|
|
result[dstInd] = (byte)((x1 << 2) | ((x2 >> 4) & 0x3));
|
|
break;
|
|
case 1:
|
|
ind = length4;
|
|
srcInd = ind * 4;
|
|
dstInd = ind * 3;
|
|
x1 = InvAlphabet[data[srcInd]];
|
|
x2 = InvAlphabet[data[srcInd + 1]];
|
|
x3 = InvAlphabet[data[srcInd + 2]];
|
|
result[dstInd] = (byte)((x1 << 2) | ((x2 >> 4) & 0x3));
|
|
result[dstInd + 1] = (byte)((x2 << 4) | ((x3 >> 2) & 0xF));
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private void EncodeBlock(byte[] src, char[] dst, int beginInd, int endInd)
|
|
{
|
|
for (int ind = beginInd; ind < endInd; ind++)
|
|
{
|
|
int srcInd = ind * 3;
|
|
int dstInd = ind * 4;
|
|
|
|
byte x1 = src[srcInd];
|
|
byte x2 = src[srcInd + 1];
|
|
byte x3 = src[srcInd + 2];
|
|
|
|
dst[dstInd] = Alphabet[x1 >> 2];
|
|
dst[dstInd + 1] = Alphabet[((x1 << 4) & 0x30) | (x2 >> 4)];
|
|
dst[dstInd + 2] = Alphabet[((x2 << 2) & 0x3C) | (x3 >> 6)];
|
|
dst[dstInd + 3] = Alphabet[x3 & 0x3F];
|
|
}
|
|
}
|
|
|
|
private void DecodeBlock(string src, byte[] dst, int beginInd, int endInd)
|
|
{
|
|
unchecked
|
|
{
|
|
for (int ind = beginInd; ind < endInd; ind++)
|
|
{
|
|
int srcInd = ind * 4;
|
|
int dstInd = ind * 3;
|
|
|
|
int x1 = InvAlphabet[src[srcInd]];
|
|
int x2 = InvAlphabet[src[srcInd + 1]];
|
|
int x3 = InvAlphabet[src[srcInd + 2]];
|
|
int x4 = InvAlphabet[src[srcInd + 3]];
|
|
|
|
dst[dstInd] = (byte)((x1 << 2) | ((x2 >> 4) & 0x3));
|
|
dst[dstInd + 1] = (byte)((x2 << 4) | ((x3 >> 2) & 0xF));
|
|
dst[dstInd + 2] = (byte)((x3 << 6) | (x4 & 0x3F));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|