update
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class Base85 : Base
|
||||
{
|
||||
public const string DefaultAlphabet = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu";
|
||||
public const char DefaultSpecial = (char)0;
|
||||
private static readonly uint[] Pow85 = { 85 * 85 * 85 * 85, 85 * 85 * 85, 85 * 85, 85, 1 };
|
||||
|
||||
public const string Prefix = "<~";
|
||||
public const string Postfix = "~>";
|
||||
|
||||
public override bool HasSpecial => false;
|
||||
|
||||
public bool PrefixPostfix { get; set; }
|
||||
|
||||
public Base85(string alphabet = DefaultAlphabet, char special = DefaultSpecial, bool prefixPostfix = false, Encoding textEncoding = null)
|
||||
: base(85, alphabet, special, textEncoding)
|
||||
{
|
||||
PrefixPostfix = prefixPostfix;
|
||||
BlockBitsCount = 32;
|
||||
BlockCharsCount = 5;
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
byte[] encodedBlock = new byte[5];
|
||||
int decodedBlockLength = 4;
|
||||
int resultLength = data.Length * (encodedBlock.Length / decodedBlockLength);
|
||||
if (PrefixPostfix)
|
||||
resultLength += Prefix.Length + Postfix.Length;
|
||||
char[] result = new char[resultLength];
|
||||
int resultInd = 0;
|
||||
|
||||
if (PrefixPostfix)
|
||||
CopyString(Prefix, result, ref resultInd);
|
||||
|
||||
int count = 0;
|
||||
uint tuple = 0;
|
||||
foreach (byte b in data)
|
||||
{
|
||||
if (count >= decodedBlockLength - 1)
|
||||
{
|
||||
tuple |= b;
|
||||
if (tuple == 0)
|
||||
result[resultInd++] = 'z';
|
||||
else
|
||||
EncodeBlock(encodedBlock.Length, result, ref resultInd, encodedBlock, tuple);
|
||||
tuple = 0;
|
||||
count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tuple |= (uint)(b << (24 - count * 8));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
EncodeBlock(count + 1, result, ref resultInd, encodedBlock, tuple);
|
||||
|
||||
if (PrefixPostfix)
|
||||
CopyString(Postfix, result, ref resultInd);
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
string dataWithoutPrefixPostfix = data;
|
||||
if (PrefixPostfix)
|
||||
{
|
||||
if (!dataWithoutPrefixPostfix.StartsWith(Prefix) || !dataWithoutPrefixPostfix.EndsWith(Postfix))
|
||||
{
|
||||
throw new Exception("ASCII85 encoded data should begin with '" + Prefix + "' and end with '" + Postfix + "'");
|
||||
}
|
||||
dataWithoutPrefixPostfix = dataWithoutPrefixPostfix.Substring(Prefix.Length, dataWithoutPrefixPostfix.Length - Prefix.Length - Postfix.Length);
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
int count = 0;
|
||||
|
||||
uint tuple = 0;
|
||||
int encodedBlockLength = 5;
|
||||
byte[] decodedBlock = new byte[4];
|
||||
foreach (char c in dataWithoutPrefixPostfix)
|
||||
{
|
||||
bool processChar;
|
||||
switch (c)
|
||||
{
|
||||
case 'z':
|
||||
if (count != 0)
|
||||
{
|
||||
throw new Exception("The character 'z' is invalid inside an ASCII85 block.");
|
||||
}
|
||||
decodedBlock[0] = 0;
|
||||
decodedBlock[1] = 0;
|
||||
decodedBlock[2] = 0;
|
||||
decodedBlock[3] = 0;
|
||||
ms.Write(decodedBlock, 0, decodedBlock.Length);
|
||||
processChar = false;
|
||||
break;
|
||||
default:
|
||||
processChar = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (processChar)
|
||||
{
|
||||
tuple += (uint)InvAlphabet[c] * Pow85[count];
|
||||
count++;
|
||||
if (count == encodedBlockLength)
|
||||
{
|
||||
DecodeBlock(decodedBlock.Length, decodedBlock, tuple);
|
||||
ms.Write(decodedBlock, 0, decodedBlock.Length);
|
||||
tuple = 0;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
throw new Exception("The last block of ASCII85 data cannot be a single byte.");
|
||||
}
|
||||
count--;
|
||||
tuple += Pow85[count];
|
||||
DecodeBlock(count, decodedBlock, tuple);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ms.WriteByte(decodedBlock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyString(string source, char[] dest, ref int destInd)
|
||||
{
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
dest[destInd++] = source[i];
|
||||
}
|
||||
|
||||
private void EncodeBlock(int count, char[] result, ref int resultInd, byte[] encodedBlock, uint tuple)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
for (int i = encodedBlock.Length - 1; i >= 0; i--)
|
||||
{
|
||||
uint quotient = tuple % 85;
|
||||
uint remainder = tuple - quotient * 85;
|
||||
tuple = quotient;
|
||||
encodedBlock[i] = (byte)remainder;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
result[resultInd++] = Alphabet[encodedBlock[i]];
|
||||
}
|
||||
}
|
||||
|
||||
private void DecodeBlock(int bytes, byte[] decodedBlock, uint tuple)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
for (int i = 0; i < bytes; i++)
|
||||
decodedBlock[i] = (byte)(tuple >> 24 - i * 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user