update
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public abstract class Base
|
||||
{
|
||||
public uint CharsCount { get; }
|
||||
|
||||
public double BitsPerChars => (double)BlockBitsCount / BlockCharsCount;
|
||||
|
||||
public int BlockBitsCount { get; protected set; }
|
||||
|
||||
public int BlockCharsCount { get; protected set; }
|
||||
|
||||
public string Alphabet { get; }
|
||||
|
||||
public char Special { get; }
|
||||
|
||||
public abstract bool HasSpecial { get; }
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public bool Parallel { get; set; }
|
||||
|
||||
protected readonly int[] InvAlphabet;
|
||||
|
||||
public Base(uint charsCount, string alphabet, char special, Encoding encoding = null, bool parallel = false)
|
||||
{
|
||||
if (alphabet.Length != charsCount)
|
||||
throw new ArgumentException($"Base string should contain {charsCount} chars");
|
||||
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
for (int j = i + 1; j < charsCount; j++)
|
||||
if (alphabet[i] == alphabet[j])
|
||||
throw new ArgumentException("Base string should contain distinct chars");
|
||||
|
||||
if (alphabet.Contains(special))
|
||||
throw new ArgumentException("Base string should not contain special char");
|
||||
|
||||
CharsCount = charsCount;
|
||||
Alphabet = alphabet;
|
||||
Special = special;
|
||||
int bitsPerChar = LogBase2(charsCount);
|
||||
BlockBitsCount = Lcm(bitsPerChar, 8);
|
||||
BlockCharsCount = BlockBitsCount / bitsPerChar;
|
||||
|
||||
InvAlphabet = new int[Alphabet.Max() + 1];
|
||||
|
||||
for (int i = 0; i < InvAlphabet.Length; i++)
|
||||
InvAlphabet[i] = -1;
|
||||
|
||||
for (int i = 0; i < charsCount; i++)
|
||||
InvAlphabet[Alphabet[i]] = i;
|
||||
|
||||
Encoding = encoding ?? Encoding.UTF8;
|
||||
Parallel = parallel;
|
||||
}
|
||||
|
||||
public virtual string EncodeString(string data)
|
||||
{
|
||||
return Encode(Encoding.GetBytes(data));
|
||||
}
|
||||
|
||||
public abstract string Encode(byte[] data);
|
||||
|
||||
public string DecodeToString(string data)
|
||||
{
|
||||
return Encoding.GetString(Decode(Regex.Replace(data, @"\r\n?|\n", "")));
|
||||
}
|
||||
|
||||
public abstract byte[] Decode(string data);
|
||||
|
||||
/// <summary>
|
||||
/// From: http://stackoverflow.com/a/600306/1046374
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPowerOf2(uint x)
|
||||
{
|
||||
uint xint = x;
|
||||
if (x - xint != 0)
|
||||
return false;
|
||||
|
||||
return (xint & (xint - 1)) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From: http://stackoverflow.com/a/13569863/1046374
|
||||
/// </summary>
|
||||
public static int Lcm(int a, int b)
|
||||
{
|
||||
int num1, num2;
|
||||
if (a > b)
|
||||
{
|
||||
num1 = a;
|
||||
num2 = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
num1 = b;
|
||||
num2 = a;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= num2; i++)
|
||||
{
|
||||
int mult = num1 * i;
|
||||
if (mult % num2 == 0)
|
||||
return mult;
|
||||
}
|
||||
|
||||
return num2;
|
||||
}
|
||||
|
||||
private static int LogBase2(uint x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((x >>= 1) != 0)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
private static int LogBaseN(uint x, uint n)
|
||||
{
|
||||
int r = 0;
|
||||
while ((x /= n) != 0)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static int GetOptimalBitsCount2(uint charsCount, out uint charsCountInBits,
|
||||
uint maxBitsCount = 64, bool base2BitsCount = false)
|
||||
{
|
||||
int result = 0;
|
||||
charsCountInBits = 0;
|
||||
int n1 = LogBase2(charsCount);
|
||||
double charsCountLog = Math.Log(2, charsCount);
|
||||
double maxRatio = 0;
|
||||
|
||||
for (int n = n1; n <= maxBitsCount; n++)
|
||||
{
|
||||
if (base2BitsCount && n % 8 != 0)
|
||||
continue;
|
||||
|
||||
uint l1 = (uint)Math.Ceiling(n * charsCountLog);
|
||||
double ratio = (double)n / l1;
|
||||
if (ratio > maxRatio)
|
||||
{
|
||||
maxRatio = ratio;
|
||||
result = n;
|
||||
charsCountInBits = l1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static int GetOptimalBitsCount(uint charsCount, out uint charsCountInBits,
|
||||
uint maxBitsCount = 64, uint radix = 2)
|
||||
{
|
||||
int result = 0;
|
||||
charsCountInBits = 0;
|
||||
int n0 = LogBaseN(charsCount, radix);
|
||||
double charsCountLog = Math.Log(radix, charsCount);
|
||||
double maxRatio = 0;
|
||||
|
||||
for (int n = n0; n <= maxBitsCount; n++)
|
||||
{
|
||||
uint k = (uint)Math.Ceiling(n * charsCountLog);
|
||||
double ratio = (double)n / k;
|
||||
if (ratio > maxRatio)
|
||||
{
|
||||
maxRatio = ratio;
|
||||
result = n;
|
||||
charsCountInBits = k;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dddac6b0e9d7d75489cf3110fcc3a611
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class Base256 : Base
|
||||
{
|
||||
public const string DefaultAlphabet = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁł";
|
||||
public const char DefaultSpecial = (char)0;
|
||||
|
||||
public override bool HasSpecial => false;
|
||||
|
||||
public Base256(string alphabet = DefaultAlphabet, char special = DefaultSpecial,
|
||||
Encoding textEncoding = null)
|
||||
: base(256, alphabet, special, textEncoding)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
var result = new char[data.Length];
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
result[i] = Alphabet[data[i]];
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
byte[] result = new byte[data.Length];
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
result[i] = (byte)InvAlphabet[data[i]];
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b84ecd8bc32acb7448d64c9bb045948d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,203 @@
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class Base32 : Base
|
||||
{
|
||||
public const string DefaultAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
public const char DefaultSpecial = '>';
|
||||
|
||||
public override bool HasSpecial => true;
|
||||
|
||||
public Base32(string alphabet = DefaultAlphabet, char special = DefaultSpecial, Encoding textEncoding = null)
|
||||
: base(32, alphabet, special, textEncoding)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
int dataLength = data.Length;
|
||||
StringBuilder result = new StringBuilder((dataLength + 4) / 5 * 8);
|
||||
|
||||
byte x1, x2;
|
||||
int i;
|
||||
|
||||
int length5 = (dataLength / 5) * 5;
|
||||
for (i = 0; i < length5; i += 5)
|
||||
{
|
||||
x1 = data[i];
|
||||
result.Append(Alphabet[x1 >> 3]);
|
||||
|
||||
x2 = data[i + 1];
|
||||
result.Append(Alphabet[((x1 << 2) & 0x1C) | (x2 >> 6)]);
|
||||
result.Append(Alphabet[(x2 >> 1) & 0x1F]);
|
||||
|
||||
x1 = data[i + 2];
|
||||
result.Append(Alphabet[((x2 << 4) & 0x10) | (x1 >> 4)]);
|
||||
|
||||
x2 = data[i + 3];
|
||||
result.Append(Alphabet[((x1 << 1) & 0x1E) | (x2 >> 7)]);
|
||||
result.Append(Alphabet[(x2 >> 2) & 0x1F]);
|
||||
|
||||
x1 = data[i + 4];
|
||||
result.Append(Alphabet[((x2 << 3) & 0x18) | (x1 >> 5)]);
|
||||
result.Append(Alphabet[x1 & 0x1F]);
|
||||
}
|
||||
|
||||
switch (dataLength - length5)
|
||||
{
|
||||
case 1:
|
||||
x1 = data[i];
|
||||
result.Append(Alphabet[x1 >> 3]);
|
||||
result.Append(Alphabet[(x1 << 2) & 0x1C]);
|
||||
|
||||
result.Append(Special, 6);
|
||||
break;
|
||||
case 2:
|
||||
x1 = data[i];
|
||||
result.Append(Alphabet[x1 >> 3]);
|
||||
x2 = data[i + 1];
|
||||
result.Append(Alphabet[((x1 << 2) & 0x1C) | (x2 >> 6)]);
|
||||
result.Append(Alphabet[(x2 >> 1) & 0x1F]);
|
||||
result.Append(Alphabet[(x2 << 4) & 0x10]);
|
||||
|
||||
result.Append(Special, 4);
|
||||
break;
|
||||
case 3:
|
||||
x1 = data[i];
|
||||
result.Append(Alphabet[x1 >> 3]);
|
||||
x2 = data[i + 1];
|
||||
result.Append(Alphabet[((x1 << 2) & 0x1C) | (x2 >> 6)]);
|
||||
result.Append(Alphabet[(x2 >> 1) & 0x1F]);
|
||||
x1 = data[i + 2];
|
||||
result.Append(Alphabet[((x2 << 4) & 0x10) | (x1 >> 4)]);
|
||||
result.Append(Alphabet[(x1 << 1) & 0x1E]);
|
||||
|
||||
result.Append(Special, 3);
|
||||
break;
|
||||
case 4:
|
||||
x1 = data[i];
|
||||
result.Append(Alphabet[x1 >> 3]);
|
||||
x2 = data[i + 1];
|
||||
result.Append(Alphabet[((x1 << 2) & 0x1C) | (x2 >> 6)]);
|
||||
result.Append(Alphabet[(x2 >> 1) & 0x1F]);
|
||||
x1 = data[i + 2];
|
||||
result.Append(Alphabet[((x2 << 4) & 0x10) | (x1 >> 4)]);
|
||||
x2 = data[i + 3];
|
||||
result.Append(Alphabet[((x1 << 1) & 0x1E) | (x2 >> 7)]);
|
||||
result.Append(Alphabet[(x2 >> 2) & 0x1F]);
|
||||
result.Append(Alphabet[(x2 << 3) & 0x18]);
|
||||
|
||||
result.Append(Special);
|
||||
break;
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
return new byte[0];
|
||||
|
||||
int additionalBytes = 0, diff = 0, tempLen = 0;
|
||||
|
||||
|
||||
int lastSpecialInd = data.Length;
|
||||
while (data[lastSpecialInd - 1] == Special)
|
||||
lastSpecialInd--;
|
||||
int tailLength = data.Length - lastSpecialInd;
|
||||
|
||||
switch (tailLength)
|
||||
{
|
||||
case 6:
|
||||
additionalBytes = 4;
|
||||
break;
|
||||
case 4:
|
||||
additionalBytes = 3;
|
||||
break;
|
||||
case 3:
|
||||
additionalBytes = 2;
|
||||
break;
|
||||
case 1:
|
||||
additionalBytes = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
diff = tailLength - additionalBytes;
|
||||
tailLength = additionalBytes;
|
||||
tempLen = data.Length - diff;
|
||||
|
||||
byte[] result = new byte[(tempLen + 7) / 8 * 5 - tailLength];
|
||||
int length5 = result.Length / 5 * 5;
|
||||
int x1, x2, x3, x4, x5, x6, x7, x8;
|
||||
|
||||
int i, srcInd = 0;
|
||||
for (i = 0; i < length5; i += 5)
|
||||
{
|
||||
x1 = InvAlphabet[data[srcInd++]];
|
||||
x2 = InvAlphabet[data[srcInd++]];
|
||||
x3 = InvAlphabet[data[srcInd++]];
|
||||
x4 = InvAlphabet[data[srcInd++]];
|
||||
x5 = InvAlphabet[data[srcInd++]];
|
||||
x6 = InvAlphabet[data[srcInd++]];
|
||||
x7 = InvAlphabet[data[srcInd++]];
|
||||
x8 = InvAlphabet[data[srcInd++]];
|
||||
|
||||
result[i] = (byte)((x1 << 3) | ((x2 >> 2) & 0x07));
|
||||
result[i + 1] = (byte)((x2 << 6) | ((x3 << 1) & 0x3E) | ((x4 >> 4) & 0x01));
|
||||
result[i + 2] = (byte)((x4 << 4) | ((x5 >> 1) & 0xF));
|
||||
result[i + 3] = (byte)((x5 << 7) | ((x6 << 2) & 0x7C) | ((x7 >> 3) & 0x03));
|
||||
result[i + 4] = (byte)((x7 << 5) | (x8 & 0x1F));
|
||||
}
|
||||
|
||||
switch (tailLength)
|
||||
{
|
||||
case 4:
|
||||
x1 = InvAlphabet[data[srcInd++]];
|
||||
x2 = InvAlphabet[data[srcInd++]];
|
||||
result[i] = (byte)((x1 << 3) | ((x2 >> 2) & 0x07));
|
||||
break;
|
||||
case 3:
|
||||
x1 = InvAlphabet[data[srcInd++]];
|
||||
x2 = InvAlphabet[data[srcInd++]];
|
||||
x3 = InvAlphabet[data[srcInd++]];
|
||||
x4 = InvAlphabet[data[srcInd++]];
|
||||
|
||||
result[i] = (byte)((x1 << 3) | ((x2 >> 2) & 0x07));
|
||||
result[i + 1] = (byte)((x2 << 6) | ((x3 << 1) & 0x3E) | ((x4 >> 4) & 0x01));
|
||||
break;
|
||||
case 2:
|
||||
x1 = InvAlphabet[data[srcInd++]];
|
||||
x2 = InvAlphabet[data[srcInd++]];
|
||||
x3 = InvAlphabet[data[srcInd++]];
|
||||
x4 = InvAlphabet[data[srcInd++]];
|
||||
x5 = InvAlphabet[data[srcInd++]];
|
||||
|
||||
result[i] = (byte)((x1 << 3) | ((x2 >> 2) & 0x07));
|
||||
result[i + 1] = (byte)((x2 << 6) | ((x3 << 1) & 0x3E) | ((x4 >> 4) & 0x01));
|
||||
result[i + 2] = (byte)((x4 << 4) | ((x5 >> 1) & 0xF));
|
||||
break;
|
||||
case 1:
|
||||
x1 = InvAlphabet[data[srcInd++]];
|
||||
x2 = InvAlphabet[data[srcInd++]];
|
||||
x3 = InvAlphabet[data[srcInd++]];
|
||||
x4 = InvAlphabet[data[srcInd++]];
|
||||
x5 = InvAlphabet[data[srcInd++]];
|
||||
x6 = InvAlphabet[data[srcInd++]];
|
||||
x7 = InvAlphabet[data[srcInd++]];
|
||||
|
||||
result[i] = (byte)((x1 << 3) | ((x2 >> 2) & 0x07));
|
||||
result[i + 1] = (byte)((x2 << 6) | ((x3 << 1) & 0x3E) | ((x4 >> 4) & 0x01));
|
||||
result[i + 2] = (byte)((x4 << 4) | ((x5 >> 1) & 0xF));
|
||||
result[i + 3] = (byte)((x5 << 7) | ((x6 << 2) & 0x7C) | ((x7 >> 3) & 0x03));
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 126c83fbe81b32444ade8272d29789b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,170 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b591d6746a6fd9b45a0174c0c22d292b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb00dc9d3e90a49439d967614c88ded8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class Base91 : Base
|
||||
{
|
||||
public const string DefaultAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\"";
|
||||
public const char DefaultSpecial = (char)0;
|
||||
|
||||
public override bool HasSpecial => false;
|
||||
|
||||
public Base91(string alphabet = DefaultAlphabet, char special = DefaultSpecial, Encoding textEncoding = null)
|
||||
: base(91, alphabet, special, textEncoding)
|
||||
{
|
||||
BlockBitsCount = 13;
|
||||
BlockCharsCount = 2;
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
StringBuilder result = new StringBuilder(data.Length);
|
||||
|
||||
int ebq = 0, en = 0;
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
ebq |= (data[i] & 255) << en;
|
||||
en += 8;
|
||||
if (en > 13)
|
||||
{
|
||||
int ev = ebq & 8191;
|
||||
|
||||
if (ev > 88)
|
||||
{
|
||||
ebq >>= 13;
|
||||
en -= 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
ev = ebq & 16383;
|
||||
ebq >>= 14;
|
||||
en -= 14;
|
||||
}
|
||||
|
||||
int quotient = Math.DivRem(ev, 91, out int remainder);
|
||||
result.Append(Alphabet[remainder]);
|
||||
result.Append(Alphabet[quotient]);
|
||||
}
|
||||
}
|
||||
|
||||
if (en > 0)
|
||||
{
|
||||
int quotient = Math.DivRem(ebq, 91, out int remainder);
|
||||
result.Append(Alphabet[remainder]);
|
||||
if (en > 7 || ebq > 90)
|
||||
result.Append(Alphabet[quotient]);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int dbq = 0, dn = 0, dv = -1;
|
||||
|
||||
List<byte> result = new List<byte>(data.Length);
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
if (InvAlphabet[data[i]] == -1)
|
||||
continue;
|
||||
if (dv == -1)
|
||||
dv = InvAlphabet[data[i]];
|
||||
else
|
||||
{
|
||||
dv += InvAlphabet[data[i]] * 91;
|
||||
dbq |= dv << dn;
|
||||
dn += (dv & 8191) > 88 ? 13 : 14;
|
||||
do
|
||||
{
|
||||
result.Add((byte)dbq);
|
||||
dbq >>= 8;
|
||||
dn -= 8;
|
||||
}
|
||||
while (dn > 7);
|
||||
dv = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dv != -1)
|
||||
result.Add((byte)(dbq | dv << dn));
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 445180477b0318d48b58549426fd8ba9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class BaseBigN : Base
|
||||
{
|
||||
private BigInteger[] _powN;
|
||||
|
||||
public bool ReverseOrder { get; }
|
||||
|
||||
public uint BlockMaxBitsCount { get; }
|
||||
|
||||
public bool MaxCompression { get; }
|
||||
|
||||
public override bool HasSpecial => false;
|
||||
|
||||
public BaseBigN(string alphabet, uint blockMaxBitsCount = 64,
|
||||
Encoding encoding = null, bool reverseOrder = false, bool parallel = false,
|
||||
bool maxCompression = false)
|
||||
: base((uint)alphabet.Length, alphabet, '\0', encoding, parallel)
|
||||
{
|
||||
BlockMaxBitsCount = blockMaxBitsCount;
|
||||
BlockBitsCount = GetOptimalBitsCount(CharsCount, out var charsCountInBits, BlockMaxBitsCount, 2);
|
||||
BlockCharsCount = (int)charsCountInBits;
|
||||
|
||||
PreparePowN(BlockCharsCount);
|
||||
|
||||
ReverseOrder = reverseOrder;
|
||||
MaxCompression = maxCompression;
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
return "";
|
||||
|
||||
int blockBitsCount, blockCharsCount;
|
||||
if (!MaxCompression)
|
||||
{
|
||||
blockBitsCount = BlockBitsCount;
|
||||
blockCharsCount = BlockCharsCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
blockBitsCount = data.Length * 8;
|
||||
blockCharsCount = (int)Math.Ceiling(blockBitsCount * Math.Log(2, Alphabet.Length));
|
||||
PreparePowN(blockCharsCount);
|
||||
}
|
||||
|
||||
int data8 = data.Length * 8;
|
||||
int mainBitsLength = data8 / blockBitsCount * blockBitsCount;
|
||||
int tailBitsLength = data8 - mainBitsLength;
|
||||
int mainCharsCount = mainBitsLength * blockCharsCount / blockBitsCount;
|
||||
int tailCharsCount = (tailBitsLength * blockCharsCount + blockBitsCount - 1) / blockBitsCount;
|
||||
int totalCharsCount = mainCharsCount + tailCharsCount;
|
||||
int iterationCount = mainCharsCount / blockCharsCount;
|
||||
|
||||
var result = new char[totalCharsCount];
|
||||
|
||||
if (!Parallel)
|
||||
EncodeBlock(data, result, 0, iterationCount, blockBitsCount, blockCharsCount);
|
||||
else
|
||||
{
|
||||
int processorCount = Math.Min(iterationCount, Environment.ProcessorCount);
|
||||
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
||||
{
|
||||
int beginInd = i * iterationCount / processorCount;
|
||||
int endInd = (i + 1) * iterationCount / processorCount;
|
||||
EncodeBlock(data, result, beginInd, endInd, blockBitsCount, blockCharsCount);
|
||||
});
|
||||
}
|
||||
|
||||
if (tailBitsLength != 0)
|
||||
{
|
||||
BigInteger bits = GetBitsN(data, mainBitsLength, tailBitsLength);
|
||||
BitsToChars(result, mainCharsCount, tailCharsCount, bits);
|
||||
}
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
return new byte[0];
|
||||
|
||||
int blockBitsCount, blockCharsCount;
|
||||
if (!MaxCompression)
|
||||
{
|
||||
blockBitsCount = BlockBitsCount;
|
||||
blockCharsCount = BlockCharsCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
blockCharsCount = data.Length;
|
||||
blockBitsCount = (int)(blockCharsCount / Math.Log(2, Alphabet.Length)) / 8 * 8;
|
||||
PreparePowN(blockCharsCount);
|
||||
}
|
||||
|
||||
int totalBitsLength = ((data.Length - 1) * blockBitsCount / blockCharsCount + 8) / 8 * 8;
|
||||
int mainBitsLength = totalBitsLength / blockBitsCount * blockBitsCount;
|
||||
int tailBitsLength = totalBitsLength - mainBitsLength;
|
||||
int mainCharsCount = mainBitsLength * blockCharsCount / blockBitsCount;
|
||||
int tailCharsCount = (tailBitsLength * blockCharsCount + blockBitsCount - 1) / blockBitsCount;
|
||||
BigInteger tailBits = CharsToBits(data, mainCharsCount, tailCharsCount);
|
||||
if (tailBits >> tailBitsLength != 0)
|
||||
{
|
||||
totalBitsLength += 8;
|
||||
mainBitsLength = totalBitsLength / blockBitsCount * blockBitsCount;
|
||||
tailBitsLength = totalBitsLength - mainBitsLength;
|
||||
mainCharsCount = mainBitsLength * blockCharsCount / blockBitsCount;
|
||||
tailCharsCount = (tailBitsLength * blockCharsCount + blockBitsCount - 1) / blockBitsCount;
|
||||
}
|
||||
int iterationCount = mainCharsCount / blockCharsCount;
|
||||
|
||||
byte[] result = new byte[totalBitsLength / 8];
|
||||
|
||||
if (!Parallel)
|
||||
{
|
||||
DecodeBlock(data, result, 0, iterationCount, blockBitsCount, blockCharsCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int processorCount = Math.Min(iterationCount, Environment.ProcessorCount);
|
||||
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
||||
{
|
||||
int beginInd = i * iterationCount / processorCount;
|
||||
int endInd = (i + 1) * iterationCount / processorCount;
|
||||
DecodeBlock(data, result, beginInd, endInd, blockBitsCount, blockCharsCount);
|
||||
});
|
||||
}
|
||||
|
||||
if (tailCharsCount != 0)
|
||||
{
|
||||
BigInteger bits = CharsToBits(data, mainCharsCount, tailCharsCount);
|
||||
AddBitsN(result, bits, mainBitsLength, tailBitsLength);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void EncodeBlock(byte[] src, char[] dst, int beginInd, int endInd, int blockBitsCount, int blockCharsCount)
|
||||
{
|
||||
for (int ind = beginInd; ind < endInd; ind++)
|
||||
{
|
||||
int charInd = ind * blockCharsCount;
|
||||
int bitInd = ind * blockBitsCount;
|
||||
BigInteger bits = GetBitsN(src, bitInd, blockBitsCount);
|
||||
BitsToChars(dst, charInd, blockCharsCount, bits);
|
||||
}
|
||||
}
|
||||
|
||||
private void DecodeBlock(string src, byte[] dst, int beginInd, int endInd, int blockBitsCount, int blockCharsCount)
|
||||
{
|
||||
for (int ind = beginInd; ind < endInd; ind++)
|
||||
{
|
||||
int charInd = ind * blockCharsCount;
|
||||
int bitInd = ind * blockBitsCount;
|
||||
BigInteger bits = CharsToBits(src, charInd, blockCharsCount);
|
||||
AddBitsN(dst, bits, bitInd, blockBitsCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static BigInteger GetBitsN(byte[] data, int bitPos, int bitsCount)
|
||||
{
|
||||
BigInteger result = 0;
|
||||
|
||||
int currentBytePos = Math.DivRem(bitPos, 8, out int currentBitInBytePos);
|
||||
int shift = 8 - currentBitInBytePos;
|
||||
int xLength = Math.Min(bitsCount, shift);
|
||||
if (xLength != 0)
|
||||
{
|
||||
result = new BigInteger((data[currentBytePos] >> shift - xLength) & ((1 << shift) - 1)) << bitsCount - xLength;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + xLength, 8, out currentBitInBytePos);
|
||||
|
||||
int x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
|
||||
while (x2Length > 0)
|
||||
{
|
||||
xLength += x2Length;
|
||||
result |= (BigInteger)(data[currentBytePos] >> 8 - x2Length) << bitsCount - xLength;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + x2Length, 8, out currentBitInBytePos);
|
||||
|
||||
x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddBitsN(byte[] data, BigInteger value, int bitPos, int bitsCount)
|
||||
{
|
||||
int currentBytePos = Math.DivRem(bitPos, 8, out int currentBitInBytePos);
|
||||
|
||||
int shift = 8 - currentBitInBytePos;
|
||||
int xLength = Math.Min(bitsCount, shift);
|
||||
if (xLength != 0)
|
||||
{
|
||||
data[currentBytePos] |= (byte) ((byte) (value >> bitsCount - shift) & ((1 << shift) - 1));
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + xLength, 8, out currentBitInBytePos);
|
||||
|
||||
int x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
|
||||
while (x2Length > 0)
|
||||
{
|
||||
xLength += x2Length;
|
||||
var bitsCountMinusXLength = bitsCount - xLength;
|
||||
data[currentBytePos] |= (byte)((value >> (bitsCountMinusXLength - 8 + x2Length)) & 0xFF);
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + x2Length, 8, out currentBitInBytePos);
|
||||
|
||||
x2Length = bitsCountMinusXLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BitsToChars(char[] chars, int ind, int count, BigInteger block)
|
||||
{
|
||||
BigInteger quotient = block;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
quotient = BigInteger.DivRem(quotient, CharsCount, out BigInteger remainder);
|
||||
chars[ind + (!ReverseOrder ? i : count - 1 - i)] = Alphabet[(int)remainder];
|
||||
}
|
||||
}
|
||||
|
||||
private BigInteger CharsToBits(string data, int ind, int count)
|
||||
{
|
||||
BigInteger result = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
result += InvAlphabet[data[ind + (!ReverseOrder ? i : count - 1 - i)]] * _powN[_powN.Length - 1 - i];
|
||||
return result;
|
||||
}
|
||||
|
||||
private void PreparePowN(int blockCharsCount)
|
||||
{
|
||||
_powN = new BigInteger[blockCharsCount];
|
||||
BigInteger pow = 1;
|
||||
for (int i = 0; i < blockCharsCount - 1; i++)
|
||||
{
|
||||
_powN[blockCharsCount - 1 - i] = pow;
|
||||
pow *= CharsCount;
|
||||
}
|
||||
if (blockCharsCount > 0)
|
||||
_powN[0] = pow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a06acee1a2bf3d4aae0b2a2b83cb5d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public class BaseN : Base
|
||||
{
|
||||
private readonly ulong[] _powN;
|
||||
|
||||
public uint BlockMaxBitsCount { get; }
|
||||
|
||||
public override bool HasSpecial => false;
|
||||
|
||||
public bool ReverseOrder { get; }
|
||||
|
||||
public BaseN(string alphabet, uint blockMaxBitsCount = 32,
|
||||
Encoding encoding = null, bool reverseOrder = false, bool parallel = false)
|
||||
: base((uint)alphabet.Length, alphabet, '\0', encoding, parallel)
|
||||
{
|
||||
BlockMaxBitsCount = blockMaxBitsCount;
|
||||
BlockBitsCount = GetOptimalBitsCount(CharsCount, out var charsCountInBits, blockMaxBitsCount);
|
||||
BlockCharsCount = (int)charsCountInBits;
|
||||
_powN = new ulong[BlockCharsCount];
|
||||
ulong pow = 1;
|
||||
for (int i = 0; i < BlockCharsCount - 1; i++)
|
||||
{
|
||||
_powN[BlockCharsCount - 1 - i] = pow;
|
||||
pow *= CharsCount;
|
||||
}
|
||||
_powN[0] = pow;
|
||||
ReverseOrder = reverseOrder;
|
||||
}
|
||||
|
||||
public override string Encode(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
return "";
|
||||
|
||||
int mainBitsLength = data.Length * 8 / BlockBitsCount * BlockBitsCount;
|
||||
int tailBitsLength = data.Length * 8 - mainBitsLength;
|
||||
int mainCharsCount = mainBitsLength * BlockCharsCount / BlockBitsCount;
|
||||
int tailCharsCount = (tailBitsLength * BlockCharsCount + BlockBitsCount - 1) / BlockBitsCount;
|
||||
int totalCharsCount = mainCharsCount + tailCharsCount;
|
||||
int iterationCount = mainCharsCount / BlockCharsCount;
|
||||
|
||||
var result = new char[totalCharsCount];
|
||||
|
||||
if (!Parallel)
|
||||
{
|
||||
EncodeBlock(data, result, 0, iterationCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int processorCount = Math.Min(iterationCount, Environment.ProcessorCount);
|
||||
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
||||
{
|
||||
int beginInd = i * iterationCount / processorCount;
|
||||
int endInd = (i + 1) * iterationCount / processorCount;
|
||||
EncodeBlock(data, result, beginInd, endInd);
|
||||
});
|
||||
}
|
||||
|
||||
if (tailBitsLength != 0)
|
||||
{
|
||||
ulong bits = GetBits64(data, mainBitsLength, tailBitsLength);
|
||||
BitsToChars(result, mainCharsCount, tailCharsCount, bits);
|
||||
}
|
||||
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
public override byte[] Decode(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
return new byte[0];
|
||||
|
||||
int totalBitsLength = ((data.Length - 1) * BlockBitsCount / BlockCharsCount + 8) / 8 * 8;
|
||||
int mainBitsLength = totalBitsLength / BlockBitsCount * BlockBitsCount;
|
||||
int tailBitsLength = totalBitsLength - mainBitsLength;
|
||||
int mainCharsCount = mainBitsLength * BlockCharsCount / BlockBitsCount;
|
||||
int tailCharsCount = (tailBitsLength * BlockCharsCount + BlockBitsCount - 1) / BlockBitsCount;
|
||||
ulong tailBits = CharsToBits(data, mainCharsCount, tailCharsCount);
|
||||
if (tailBits >> tailBitsLength != 0)
|
||||
{
|
||||
totalBitsLength += 8;
|
||||
mainBitsLength = totalBitsLength / BlockBitsCount * BlockBitsCount;
|
||||
tailBitsLength = totalBitsLength - mainBitsLength;
|
||||
mainCharsCount = mainBitsLength * BlockCharsCount / BlockBitsCount;
|
||||
tailCharsCount = (tailBitsLength * BlockCharsCount + BlockBitsCount - 1) / BlockBitsCount;
|
||||
}
|
||||
int iterationCount = mainCharsCount / BlockCharsCount;
|
||||
|
||||
byte[] result = new byte[totalBitsLength / 8];
|
||||
|
||||
if (!Parallel)
|
||||
{
|
||||
DecodeBlock(data, result, 0, iterationCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int processorCount = Math.Min(iterationCount, Environment.ProcessorCount);
|
||||
System.Threading.Tasks.Parallel.For(0, processorCount, i =>
|
||||
{
|
||||
int beginInd = i * iterationCount / processorCount;
|
||||
int endInd = (i + 1) * iterationCount / processorCount;
|
||||
DecodeBlock(data, result, beginInd, endInd);
|
||||
});
|
||||
}
|
||||
|
||||
if (tailCharsCount != 0)
|
||||
{
|
||||
ulong bits = CharsToBits(data, mainCharsCount, tailCharsCount);
|
||||
AddBits64(result, bits, mainBitsLength, tailBitsLength);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void EncodeBlock(byte[] src, char[] dst, int beginInd, int endInd)
|
||||
{
|
||||
for (int ind = beginInd; ind < endInd; ind++)
|
||||
{
|
||||
int charInd = ind * BlockCharsCount;
|
||||
int bitInd = ind * BlockBitsCount;
|
||||
ulong bits = GetBits64(src, bitInd, BlockBitsCount);
|
||||
BitsToChars(dst, charInd, BlockCharsCount, bits);
|
||||
}
|
||||
}
|
||||
|
||||
private void DecodeBlock(string src, byte[] dst, int beginInd, int endInd)
|
||||
{
|
||||
for (int ind = beginInd; ind < endInd; ind++)
|
||||
{
|
||||
int charInd = ind * BlockCharsCount;
|
||||
int bitInd = ind * BlockBitsCount;
|
||||
ulong bits = CharsToBits(src, charInd, BlockCharsCount);
|
||||
AddBits64(dst, bits, bitInd, BlockBitsCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetBits64(byte[] data, int bitPos, int bitsCount)
|
||||
{
|
||||
ulong result = 0;
|
||||
|
||||
int currentBytePos = Math.DivRem(bitPos, 8, out int currentBitInBytePos);
|
||||
|
||||
int xLength = Math.Min(bitsCount, 8 - currentBitInBytePos);
|
||||
if (xLength != 0)
|
||||
{
|
||||
result = ((ulong)data[currentBytePos] << 56 + currentBitInBytePos) >> 64 - xLength << bitsCount - xLength;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + xLength, 8, out currentBitInBytePos);
|
||||
|
||||
int x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
|
||||
while (x2Length > 0)
|
||||
{
|
||||
xLength += x2Length;
|
||||
result |= (ulong)data[currentBytePos] >> 8 - x2Length << bitsCount - xLength;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + x2Length, 8, out currentBitInBytePos);
|
||||
|
||||
x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddBits64(byte[] data, ulong value, int bitPos, int bitsCount)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int currentBytePos = Math.DivRem(bitPos, 8, out int currentBitInBytePos);
|
||||
|
||||
int xLength = Math.Min(bitsCount, 8 - currentBitInBytePos);
|
||||
if (xLength != 0)
|
||||
{
|
||||
byte x1 = (byte)(value << 64 - bitsCount >> 56 + currentBitInBytePos);
|
||||
data[currentBytePos] |= x1;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + xLength, 8, out currentBitInBytePos);
|
||||
|
||||
int x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
|
||||
while (x2Length > 0)
|
||||
{
|
||||
xLength += x2Length;
|
||||
byte x2 = (byte)(value >> bitsCount - xLength << 8 - x2Length);
|
||||
data[currentBytePos] |= x2;
|
||||
|
||||
currentBytePos += Math.DivRem(currentBitInBytePos + x2Length, 8, out currentBitInBytePos);
|
||||
|
||||
x2Length = bitsCount - xLength;
|
||||
if (x2Length > 8)
|
||||
x2Length = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BitsToChars(char[] chars, int ind, int count, ulong block)
|
||||
{
|
||||
ulong result = block;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ulong quotient = result / CharsCount;
|
||||
ulong remainder = result - quotient * CharsCount;
|
||||
result = quotient;
|
||||
chars[ind + (!ReverseOrder ? i : count - 1 - i)] = Alphabet[(int)remainder];
|
||||
}
|
||||
}
|
||||
|
||||
private ulong CharsToBits(string data, int ind, int count)
|
||||
{
|
||||
ulong result = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
result += (ulong)InvAlphabet[data[ind + (!ReverseOrder ? i : count - 1 - i)]] * _powN[BlockCharsCount - 1 - i];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d996e73fb18bfd45be33725dcf13786
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace BaseNcoding
|
||||
{
|
||||
public static class StringGenerator
|
||||
{
|
||||
private const string LettersAndDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
public static string GetAlphabet(int charsCount)
|
||||
{
|
||||
var result = new StringBuilder(charsCount);
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
do
|
||||
{
|
||||
char c = (char)i;
|
||||
if (!char.IsControl(c) && !char.IsWhiteSpace(c))
|
||||
{
|
||||
result.Append(c);
|
||||
count++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (count < charsCount);
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static string GetRandom(int size, bool onlyLettersAndDigits)
|
||||
{
|
||||
Random r = new Random();
|
||||
if (onlyLettersAndDigits)
|
||||
{
|
||||
var result = new char[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
result[i] = LettersAndDigits[r.Next(LettersAndDigits.Length)];
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
var data = new byte[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
data[i] = (byte) r.Next(32, 127);
|
||||
var encoding = new ASCIIEncoding();
|
||||
return encoding.GetString(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b72d6d0bcd1a9e846ad04cc26014bbcd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user