diff --git a/Assets/Scenes/Version2.unity b/Assets/Scenes/Version2.unity index 119b79b..269dacf 100644 --- a/Assets/Scenes/Version2.unity +++ b/Assets/Scenes/Version2.unity @@ -30122,6 +30122,7 @@ GameObject: - component: {fileID: 1564959565} - component: {fileID: 1564959567} - component: {fileID: 1564959568} + - component: {fileID: 1564959569} m_Layer: 0 m_Name: ViewerMain m_TagString: Untagged @@ -30275,6 +30276,18 @@ MonoBehaviour: type: 2} AnimationCameraAnimator: {fileID: 1468470379} AnimationCamera: {fileID: 790387849} +--- !u!114 &1564959569 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1564959564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 822adc4dd85200142901586dd5c697c6, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1566898429 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/BaseNcoding.meta b/Assets/Scripts/BaseNcoding.meta new file mode 100644 index 0000000..1cf72b5 --- /dev/null +++ b/Assets/Scripts/BaseNcoding.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8efe8ce6fc7c3d84db812df72e79c7ce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base.cs b/Assets/Scripts/BaseNcoding/Base.cs new file mode 100644 index 0000000..8dd9dfc --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base.cs @@ -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); + + /// + /// From: http://stackoverflow.com/a/600306/1046374 + /// + /// + /// + public static bool IsPowerOf2(uint x) + { + uint xint = x; + if (x - xint != 0) + return false; + + return (xint & (xint - 1)) == 0; + } + + /// + /// From: http://stackoverflow.com/a/13569863/1046374 + /// + 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; + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base.cs.meta b/Assets/Scripts/BaseNcoding/Base.cs.meta new file mode 100644 index 0000000..904a5d4 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dddac6b0e9d7d75489cf3110fcc3a611 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base256.cs b/Assets/Scripts/BaseNcoding/Base256.cs new file mode 100644 index 0000000..8a90ef2 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base256.cs @@ -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; + } + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base256.cs.meta b/Assets/Scripts/BaseNcoding/Base256.cs.meta new file mode 100644 index 0000000..5d61cf6 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base256.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b84ecd8bc32acb7448d64c9bb045948d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base32.cs b/Assets/Scripts/BaseNcoding/Base32.cs new file mode 100644 index 0000000..33b70ff --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base32.cs @@ -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; + } + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base32.cs.meta b/Assets/Scripts/BaseNcoding/Base32.cs.meta new file mode 100644 index 0000000..b799a44 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 126c83fbe81b32444ade8272d29789b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base64.cs b/Assets/Scripts/BaseNcoding/Base64.cs new file mode 100644 index 0000000..302be4f --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base64.cs @@ -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)); + } + } + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base64.cs.meta b/Assets/Scripts/BaseNcoding/Base64.cs.meta new file mode 100644 index 0000000..a866f2e --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b591d6746a6fd9b45a0174c0c22d292b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base85.cs b/Assets/Scripts/BaseNcoding/Base85.cs new file mode 100644 index 0000000..ce385ae --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base85.cs @@ -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); + } + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base85.cs.meta b/Assets/Scripts/BaseNcoding/Base85.cs.meta new file mode 100644 index 0000000..a3cfa13 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base85.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb00dc9d3e90a49439d967614c88ded8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/Base91.cs b/Assets/Scripts/BaseNcoding/Base91.cs new file mode 100644 index 0000000..e1d1a8d --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base91.cs @@ -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 result = new List(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(); + } + } + } +} diff --git a/Assets/Scripts/BaseNcoding/Base91.cs.meta b/Assets/Scripts/BaseNcoding/Base91.cs.meta new file mode 100644 index 0000000..b9ab053 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/Base91.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 445180477b0318d48b58549426fd8ba9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/BaseBigN.cs b/Assets/Scripts/BaseNcoding/BaseBigN.cs new file mode 100644 index 0000000..a862647 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/BaseBigN.cs @@ -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; + } + } +} diff --git a/Assets/Scripts/BaseNcoding/BaseBigN.cs.meta b/Assets/Scripts/BaseNcoding/BaseBigN.cs.meta new file mode 100644 index 0000000..e383bc8 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/BaseBigN.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a06acee1a2bf3d4aae0b2a2b83cb5d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/BaseN.cs b/Assets/Scripts/BaseNcoding/BaseN.cs new file mode 100644 index 0000000..6a1c011 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/BaseN.cs @@ -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; + } + } +} diff --git a/Assets/Scripts/BaseNcoding/BaseN.cs.meta b/Assets/Scripts/BaseNcoding/BaseN.cs.meta new file mode 100644 index 0000000..4b0cada --- /dev/null +++ b/Assets/Scripts/BaseNcoding/BaseN.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d996e73fb18bfd45be33725dcf13786 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/BaseNcoding/StringGenerator.cs b/Assets/Scripts/BaseNcoding/StringGenerator.cs new file mode 100644 index 0000000..339a396 --- /dev/null +++ b/Assets/Scripts/BaseNcoding/StringGenerator.cs @@ -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); + } + } +} diff --git a/Assets/Scripts/BaseNcoding/StringGenerator.cs.meta b/Assets/Scripts/BaseNcoding/StringGenerator.cs.meta new file mode 100644 index 0000000..df242cc --- /dev/null +++ b/Assets/Scripts/BaseNcoding/StringGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b72d6d0bcd1a9e846ad04cc26014bbcd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4.meta b/Assets/Scripts/LZ4.meta new file mode 100644 index 0000000..50baab7 --- /dev/null +++ b/Assets/Scripts/LZ4.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dbb2fcf97a52a74408ada473a663544b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe.cs b/Assets/Scripts/LZ4/LZ4Codec.Safe.cs new file mode 100644 index 0000000..7b55e92 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe.cs @@ -0,0 +1,757 @@ +#region license + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +using System; +using System.Diagnostics; + +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming + +namespace LZ4ps +{ + /// Safe LZ4 codec. + public static partial class LZ4Codec + { + #region Helper + + // ReSharper disable UnusedParameter.Local + + [Conditional("DEBUG")] + private static void Assert(bool condition, string errorMessage) + { + if (!condition) throw new ArgumentException(errorMessage); + Debug.Assert(condition, errorMessage); + } + + // ReSharper restore UnusedParameter.Local + + #endregion + + #region Byte manipulation + + // ReSharper disable RedundantCast + + internal static void Poke2(byte[] buffer, int offset, ushort value) + { + buffer[offset] = (byte)value; + buffer[offset + 1] = (byte)(value >> 8); + } + + internal static ushort Peek2(byte[] buffer, int offset) + { + // NOTE: It's faster than BitConverter.ToUInt16 (suprised? me too) + return (ushort)(((uint)buffer[offset]) | ((uint)buffer[offset + 1] << 8)); + } + + internal static uint Peek4(byte[] buffer, int offset) + { + // NOTE: It's faster than BitConverter.ToUInt32 (suprised? me too) + return + ((uint)buffer[offset]) | + ((uint)buffer[offset + 1] << 8) | + ((uint)buffer[offset + 2] << 16) | + ((uint)buffer[offset + 3] << 24); + } + + private static uint Xor4(byte[] buffer, int offset1, int offset2) + { + // return Peek4(buffer, offset1) ^ Peek4(buffer, offset2); + var value1 = + ((uint)buffer[offset1]) | + ((uint)buffer[offset1 + 1] << 8) | + ((uint)buffer[offset1 + 2] << 16) | + ((uint)buffer[offset1 + 3] << 24); + var value2 = + ((uint)buffer[offset2]) | + ((uint)buffer[offset2 + 1] << 8) | + ((uint)buffer[offset2 + 2] << 16) | + ((uint)buffer[offset2 + 3] << 24); + return value1 ^ value2; + } + + private static ulong Xor8(byte[] buffer, int offset1, int offset2) + { + // return Peek8(buffer, offset1) ^ Peek8(buffer, offset2); + var value1 = + ((ulong)buffer[offset1]) | + ((ulong)buffer[offset1 + 1] << 8) | + ((ulong)buffer[offset1 + 2] << 16) | + ((ulong)buffer[offset1 + 3] << 24) | + ((ulong)buffer[offset1 + 4] << 32) | + ((ulong)buffer[offset1 + 5] << 40) | + ((ulong)buffer[offset1 + 6] << 48) | + ((ulong)buffer[offset1 + 7] << 56); + var value2 = + ((ulong)buffer[offset2]) | + ((ulong)buffer[offset2 + 1] << 8) | + ((ulong)buffer[offset2 + 2] << 16) | + ((ulong)buffer[offset2 + 3] << 24) | + ((ulong)buffer[offset2 + 4] << 32) | + ((ulong)buffer[offset2 + 5] << 40) | + ((ulong)buffer[offset2 + 6] << 48) | + ((ulong)buffer[offset2 + 7] << 56); + return value1 ^ value2; + } + + private static bool Equal2(byte[] buffer, int offset1, int offset2) + { + // return Peek2(buffer, offset1) == Peek2(buffer, offset2); + if (buffer[offset1] != buffer[offset2]) return false; + return buffer[offset1 + 1] == buffer[offset2 + 1]; + } + + private static bool Equal4(byte[] buffer, int offset1, int offset2) + { + // return Peek4(buffer, offset1) == Peek4(buffer, offset2); + if (buffer[offset1] != buffer[offset2]) return false; + if (buffer[offset1 + 1] != buffer[offset2 + 1]) return false; + if (buffer[offset1 + 2] != buffer[offset2 + 2]) return false; + return buffer[offset1 + 3] == buffer[offset2 + 3]; + } + + // ReSharper restore RedundantCast + + #endregion + + #region Byte block copy + + private static void Copy4(byte[] buf, int src, int dst) + { + Assert(dst > src, "Copying backwards is not implemented"); + buf[dst + 3] = buf[src + 3]; + buf[dst + 2] = buf[src + 2]; + buf[dst + 1] = buf[src + 1]; + buf[dst] = buf[src]; + } + + private static void Copy8(byte[] buf, int src, int dst) + { + Assert(dst > src, "Copying backwards is not implemented"); + buf[dst + 7] = buf[src + 7]; + buf[dst + 6] = buf[src + 6]; + buf[dst + 5] = buf[src + 5]; + buf[dst + 4] = buf[src + 4]; + buf[dst + 3] = buf[src + 3]; + buf[dst + 2] = buf[src + 2]; + buf[dst + 1] = buf[src + 1]; + buf[dst] = buf[src]; + } + + private static void BlockCopy(byte[] src, int src_0, byte[] dst, int dst_0, int len) + { + Assert(src != dst, "BlockCopy does not handle copying to the same buffer"); + + if (len >= BLOCK_COPY_LIMIT) + { + Buffer.BlockCopy(src, src_0, dst, dst_0, len); + } + else + { + while (len >= 8) + { + dst[dst_0] = src[src_0]; + dst[dst_0 + 1] = src[src_0 + 1]; + dst[dst_0 + 2] = src[src_0 + 2]; + dst[dst_0 + 3] = src[src_0 + 3]; + dst[dst_0 + 4] = src[src_0 + 4]; + dst[dst_0 + 5] = src[src_0 + 5]; + dst[dst_0 + 6] = src[src_0 + 6]; + dst[dst_0 + 7] = src[src_0 + 7]; + len -= 8; + src_0 += 8; + dst_0 += 8; + } + + while (len >= 4) + { + dst[dst_0] = src[src_0]; + dst[dst_0 + 1] = src[src_0 + 1]; + dst[dst_0 + 2] = src[src_0 + 2]; + dst[dst_0 + 3] = src[src_0 + 3]; + len -= 4; + src_0 += 4; + dst_0 += 4; + } + + while (len-- > 0) + { + dst[dst_0++] = src[src_0++]; + } + } + } + + private static int WildCopy(byte[] src, int src_0, byte[] dst, int dst_0, int dst_end) + { + var len = dst_end - dst_0; + + Assert(src != dst, "BlockCopy does not handle copying to the same buffer"); + Assert(len > 0, "Length have to be greater than 0"); + + if (len >= BLOCK_COPY_LIMIT) + { + Buffer.BlockCopy(src, src_0, dst, dst_0, len); + } + else + { + // apparently (tested) this is an overkill + // it seems to be faster without this 8-byte loop + //while (len >= 8) + //{ + // dst[dst_0] = src[src_0]; + // dst[dst_0 + 1] = src[src_0 + 1]; + // dst[dst_0 + 2] = src[src_0 + 2]; + // dst[dst_0 + 3] = src[src_0 + 3]; + // dst[dst_0 + 4] = src[src_0 + 4]; + // dst[dst_0 + 5] = src[src_0 + 5]; + // dst[dst_0 + 6] = src[src_0 + 6]; + // dst[dst_0 + 7] = src[src_0 + 7]; + // len -= 8; src_0 += 8; dst_0 += 8; + //} + + while (len >= 4) + { + dst[dst_0] = src[src_0]; + dst[dst_0 + 1] = src[src_0 + 1]; + dst[dst_0 + 2] = src[src_0 + 2]; + dst[dst_0 + 3] = src[src_0 + 3]; + len -= 4; + src_0 += 4; + dst_0 += 4; + } + + while (len-- > 0) + { + dst[dst_0++] = src[src_0++]; + } + } + + return len; + } + + private static int SecureCopy(byte[] buffer, int src, int dst, int dst_end) + { + var diff = dst - src; + var length = dst_end - dst; + var len = length; + + Assert(diff >= 4, "Target must be at least 4 bytes further than source"); + Assert(BLOCK_COPY_LIMIT > 4, "This method requires BLOCK_COPY_LIMIT > 4"); + Assert(len > 0, "Length have to be greater than 0"); + + if (diff >= BLOCK_COPY_LIMIT) + { + if (diff >= length) + { + Buffer.BlockCopy(buffer, src, buffer, dst, length); + return length; // done + } + + do + { + Buffer.BlockCopy(buffer, src, buffer, dst, diff); + src += diff; + dst += diff; + len -= diff; + } while (len >= diff); + } + + // apparently (tested) this is an overkill + // it seems to be faster without this 8-byte loop + //while (len >= 8) + //{ + // buffer[dst] = buffer[src]; + // buffer[dst + 1] = buffer[src + 1]; + // buffer[dst + 2] = buffer[src + 2]; + // buffer[dst + 3] = buffer[src + 3]; + // buffer[dst + 4] = buffer[src + 4]; + // buffer[dst + 5] = buffer[src + 5]; + // buffer[dst + 6] = buffer[src + 6]; + // buffer[dst + 7] = buffer[src + 7]; + // dst += 8; src += 8; len -= 8; + //} + + while (len >= 4) + { + buffer[dst] = buffer[src]; + buffer[dst + 1] = buffer[src + 1]; + buffer[dst + 2] = buffer[src + 2]; + buffer[dst + 3] = buffer[src + 3]; + dst += 4; + src += 4; + len -= 4; + } + + while (len-- > 0) + { + buffer[dst++] = buffer[src++]; + } + + return length; // done + } + + #endregion + + #region Encode32 + + /// Encodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Number of bytes written. + public static int Encode32( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength) + { + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + if (outputLength == 0) return 0; + + if (inputLength < LZ4_64KLIMIT) + { + var hashTable = new ushort[HASH64K_TABLESIZE]; + return LZ4_compress64kCtx_safe32(hashTable, input, output, inputOffset, outputOffset, inputLength, outputLength); + } + else + { + var hashTable = new int[HASH_TABLESIZE]; + return LZ4_compressCtx_safe32(hashTable, input, output, inputOffset, outputOffset, inputLength, outputLength); + } + } + + /// Encodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// Compressed buffer. + public static byte[] Encode32(byte[] input, int inputOffset, int inputLength) + { + if (inputLength < 0) inputLength = input.Length - inputOffset; + + if (input == null) throw new ArgumentNullException("input"); + if (inputOffset < 0 || inputOffset + inputLength > input.Length) + throw new ArgumentException("inputOffset and inputLength are invalid for given input"); + + var result = new byte[MaximumOutputLength(inputLength)]; + var length = Encode32(input, inputOffset, inputLength, result, 0, result.Length); + + if (length != result.Length) + { + if (length < 0) + throw new InvalidOperationException("Compression has been corrupted"); + var buffer = new byte[length]; + Buffer.BlockCopy(result, 0, buffer, 0, length); + return buffer; + } + return result; + } + + #endregion + + #region Encode64 + + /// Encodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Number of bytes written. + public static int Encode64( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength) + { + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + if (outputLength == 0) return 0; + + if (inputLength < LZ4_64KLIMIT) + { + var hashTable = new ushort[HASH64K_TABLESIZE]; + return LZ4_compress64kCtx_safe64(hashTable, input, output, inputOffset, outputOffset, inputLength, outputLength); + } + else + { + var hashTable = new int[HASH_TABLESIZE]; + return LZ4_compressCtx_safe64(hashTable, input, output, inputOffset, outputOffset, inputLength, outputLength); + } + } + + /// Encodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// Compressed buffer. + public static byte[] Encode64(byte[] input, int inputOffset, int inputLength) + { + if (inputLength < 0) inputLength = input.Length - inputOffset; + + if (input == null) throw new ArgumentNullException("input"); + if (inputOffset < 0 || inputOffset + inputLength > input.Length) + throw new ArgumentException("inputOffset and inputLength are invalid for given input"); + + var result = new byte[MaximumOutputLength(inputLength)]; + var length = Encode64(input, inputOffset, inputLength, result, 0, result.Length); + + if (length != result.Length) + { + if (length < 0) + throw new InvalidOperationException("Compression has been corrupted"); + var buffer = new byte[length]; + Buffer.BlockCopy(result, 0, buffer, 0, length); + return buffer; + } + return result; + } + + #endregion + + #region Decode32 + + /// Decodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Set it to true if output length is known. + /// Number of bytes written. + public static int Decode32( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength, + bool knownOutputLength) + { + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + + if (outputLength == 0) return 0; + + if (knownOutputLength) + { + var length = LZ4_uncompress_safe32(input, output, inputOffset, outputOffset, outputLength); + if (length != inputLength) + throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); + return outputLength; + } + else + { + var length = LZ4_uncompress_unknownOutputSize_safe32(input, output, inputOffset, outputOffset, inputLength, outputLength); + if (length < 0) + throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); + return length; + } + } + + /// Decodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// Length of the output. + /// Decompressed buffer. + public static byte[] Decode32(byte[] input, int inputOffset, int inputLength, int outputLength) + { + if (inputLength < 0) inputLength = input.Length - inputOffset; + + if (input == null) throw new ArgumentNullException("input"); + if (inputOffset < 0 || inputOffset + inputLength > input.Length) + throw new ArgumentException("inputOffset and inputLength are invalid for given input"); + + var result = new byte[outputLength]; + var length = Decode32(input, inputOffset, inputLength, result, 0, outputLength, true); + if (length != outputLength) + throw new ArgumentException("outputLength is not valid"); + return result; + } + + #endregion + + #region Decode64 + + /// Decodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Set it to true if output length is known. + /// Number of bytes written. + public static int Decode64( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength, + bool knownOutputLength) + { + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + + if (outputLength == 0) return 0; + + if (knownOutputLength) + { + var length = LZ4_uncompress_safe64(input, output, inputOffset, outputOffset, outputLength); + if (length != inputLength) + throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); + return outputLength; + } + else + { + var length = LZ4_uncompress_unknownOutputSize_safe64(input, output, inputOffset, outputOffset, inputLength, outputLength); + if (length < 0) + throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); + return length; + } + } + + /// Decodes the specified input. + /// The input. + /// The input offset. + /// Length of the input. + /// Length of the output. + /// Decompressed buffer. + public static byte[] Decode64(byte[] input, int inputOffset, int inputLength, int outputLength) + { + if (inputLength < 0) inputLength = input.Length - inputOffset; + + if (input == null) throw new ArgumentNullException("input"); + if (inputOffset < 0 || inputOffset + inputLength > input.Length) + throw new ArgumentException("inputOffset and inputLength are invalid for given input"); + + var result = new byte[outputLength]; + var length = Decode64(input, inputOffset, inputLength, result, 0, outputLength, true); + if (length != outputLength) + throw new ArgumentException("outputLength is not valid"); + return result; + } + + #endregion + + #region HC utilities + + // ReSharper disable InconsistentNaming + + private class LZ4HC_Data_Structure + { + public byte[] src; + public int src_base; + public int src_end; + public int src_LASTLITERALS; + public byte[] dst; + public int dst_base; + public int dst_len; + public int dst_end; + public int[] hashTable; + public ushort[] chainTable; + public int nextToUpdate; + }; + + // ReSharper restore InconsistentNaming + + + private static LZ4HC_Data_Structure LZ4HC_Create(byte[] src, int src_0, int src_len, byte[] dst, int dst_0, int dst_len) + { + var hc4 = new LZ4HC_Data_Structure { + src = src, + src_base = src_0, + src_end = src_0 + src_len, + src_LASTLITERALS = (src_0 + src_len - LASTLITERALS), + dst = dst, + dst_base = dst_0, + dst_len = dst_len, + dst_end = dst_0 + dst_len, + hashTable = new int[HASHHC_TABLESIZE], + chainTable = new ushort[MAXD], + nextToUpdate = src_0 + 1, + }; + + var ct = hc4.chainTable; + for (var i = ct.Length - 1; i >= 0; i--) ct[i] = unchecked((ushort)-1); + + return hc4; + } + + #endregion + + #region Encode32HC + + private static int LZ4_compressHC_32( + byte[] input, int inputOffset, int inputLength, + byte[] output, int outputOffset, int outputLength) + { + return LZ4_compressHCCtx_32( + LZ4HC_Create( + input, inputOffset, inputLength, output, outputOffset, outputLength)); + } + + /// Encodes the specified input using HC codec. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Number of bytes written. NOTE: when output buffer is too small it returns negative value. + public static int Encode32HC( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength) + { + if (inputLength == 0) return 0; + + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + + var length = LZ4_compressHC_32(input, inputOffset, inputLength, output, outputOffset, outputLength); + // NOTE: there is a potential problem here as original implementation returns 0 not -1 + return length <= 0 ? -1 : length; + } + + /// Encodes the specified input using HC codec. + /// The input. + /// The input offset. + /// Length of the input. + /// Buffer with compressed data (NOTE: it can be bigger than input). + public static byte[] Encode32HC( + byte[] input, int inputOffset, int inputLength) + { + if (inputLength == 0) return new byte[0]; + var outputLength = MaximumOutputLength(inputLength); + var result = new byte[outputLength]; + var length = Encode32HC(input, inputOffset, inputLength, result, 0, outputLength); + + if (length < 0) + throw new ArgumentException("Provided data seems to be corrupted."); + + if (length != outputLength) + { + var buffer = new byte[length]; + Buffer.BlockCopy(result, 0, buffer, 0, length); + result = buffer; + } + + return result; + } + + #endregion + + #region Encode64HC + + private static int LZ4_compressHC_64( + byte[] input, int inputOffset, int inputLength, + byte[] output, int outputOffset, int outputLength) + { + return LZ4_compressHCCtx_64( + LZ4HC_Create( + input, inputOffset, inputLength, output, outputOffset, outputLength)); + } + + /// Encodes the specified input using HC codec. + /// The input. + /// The input offset. + /// Length of the input. + /// The output. + /// The output offset. + /// Length of the output. + /// Number of bytes written. NOTE: when output buffer is too small it returns negative value. + public static int Encode64HC( + byte[] input, + int inputOffset, + int inputLength, + byte[] output, + int outputOffset, + int outputLength) + { + if (inputLength == 0) return 0; + + CheckArguments( + input, inputOffset, ref inputLength, + output, outputOffset, ref outputLength); + + var length = LZ4_compressHC_64(input, inputOffset, inputLength, output, outputOffset, outputLength); + // NOTE: there is a potential problem here as original implementation returns 0 not -1 + return length <= 0 ? -1 : length; + } + + /// Encodes the specified input using HC codec. + /// The input. + /// The input offset. + /// Length of the input. + /// Buffer with compressed data (NOTE: it can be bigger than input). + public static byte[] Encode64HC( + byte[] input, int inputOffset, int inputLength) + { + if (inputLength == 0) return new byte[0]; + var outputLength = MaximumOutputLength(inputLength); + var result = new byte[outputLength]; + var length = Encode64HC(input, inputOffset, inputLength, result, 0, outputLength); + + if (length < 0) + throw new ArgumentException("Provided data seems to be corrupted."); + + if (length != outputLength) + { + var buffer = new byte[length]; + Buffer.BlockCopy(result, 0, buffer, 0, length); + result = buffer; + } + + return result; + } + + #endregion + } +} + +// ReSharper restore InconsistentNaming +// ReSharper restore CheckNamespace diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.Safe.cs.meta new file mode 100644 index 0000000..c5fc009 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2f5d2c7e152d8f43ab710530107c9ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs b/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs new file mode 100644 index 0000000..85e7c49 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs @@ -0,0 +1,795 @@ +#region LZ4 original + +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2012, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + - LZ4 source repository : http://code.google.com/p/lz4/ +*/ + +#endregion + +#region LZ4 port + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable TooWideLocalVariableScope +// ReSharper disable JoinDeclarationAndInitializer +// ReSharper disable RedundantIfElseBlock + +namespace LZ4ps +{ + public static partial class LZ4Codec + { + #region LZ4_compressCtx + + private static int LZ4_compressCtx_safe32( + int[] hash_table, + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var debruijn32 = DEBRUIJN_TABLE_32; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_base = src_0; + var src_anchor = src_p; + var src_end = src_p + src_len; + var src_mflimit = src_end - MFLIMIT; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + + var src_LASTLITERALS = src_end - LASTLITERALS; + var src_LASTLITERALS_1 = src_LASTLITERALS - 1; + + var src_LASTLITERALS_STEPSIZE_1 = src_LASTLITERALS - (STEPSIZE_32 - 1); + var dst_LASTLITERALS_1 = dst_end - (1 + LASTLITERALS); + var dst_LASTLITERALS_3 = dst_end - (2 + 1 + LASTLITERALS); + + int length; + + uint h, h_fwd; + + // Init + if (src_len < MINLENGTH) goto _last_literals; + + // First Byte + hash_table[(((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST)] = (src_p - src_base); + src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + + // Main Loop + while (true) + { + var findMatchAttempts = (1 << SKIPSTRENGTH) + 3; + var src_p_fwd = src_p; + int src_ref; + int dst_token; + + // Find a match + do + { + h = h_fwd; + var step = findMatchAttempts++ >> SKIPSTRENGTH; + src_p = src_p_fwd; + src_p_fwd = src_p + step; + + if (src_p_fwd > src_mflimit) goto _last_literals; + + h_fwd = (((Peek4(src, src_p_fwd)) * 2654435761u) >> HASH_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (src_p - src_base); + } while ((src_ref < src_p - MAX_DISTANCE) || (!Equal4(src, src_ref, src_p))); + + // Catch up + while ((src_p > src_anchor) && (src_ref > src_0) && (src[src_p - 1] == src[src_ref - 1])) + { + src_p--; + src_ref--; + } + + // Encode Literal length + length = (src_p - src_anchor); + dst_token = dst_p++; + + if (dst_p + length + (length >> 8) > dst_LASTLITERALS_3) return 0; // Check output limit + + if (length >= RUN_MASK) + { + var len = length - RUN_MASK; + dst[dst_token] = (RUN_MASK << ML_BITS); + if (len > 254) + { + do + { + dst[dst_p++] = 255; + len -= 255; + } while (len > 254); + dst[dst_p++] = (byte)len; + BlockCopy(src, src_anchor, dst, dst_p, length); + dst_p += length; + goto _next_match; + } + else + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) + { + _i = dst_p + length; + WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + _next_match: + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Start Counting + src_p += MINMATCH; + src_ref += MINMATCH; // MinMatch already verified + src_anchor = src_p; + + while (src_p < src_LASTLITERALS_STEPSIZE_1) + { + var diff = (int)Xor4(src, src_ref, src_p); + if (diff == 0) + { + src_p += STEPSIZE_32; + src_ref += STEPSIZE_32; + continue; + } + src_p += debruijn32[((uint)((diff) & -(diff)) * 0x077CB531u) >> 27]; + goto _endCount; + } + + if ((src_p < src_LASTLITERALS_1) && (Equal2(src, src_ref, src_p))) + { + src_p += 2; + src_ref += 2; + } + if ((src_p < src_LASTLITERALS) && (src[src_ref] == src[src_p])) src_p++; + + _endCount: + // Encode MatchLength + length = (src_p - src_anchor); + + if (dst_p + (length >> 8) > dst_LASTLITERALS_1) return 0; // Check output limit + + if (length >= ML_MASK) + { + dst[dst_token] += ML_MASK; + length -= ML_MASK; + for (; length > 509; length -= 510) + { + dst[dst_p++] = 255; + dst[dst_p++] = 255; + } + if (length > 254) + { + length -= 255; + dst[dst_p++] = 255; + } + dst[dst_p++] = (byte)length; + } + else + { + dst[dst_token] += (byte)length; + } + + // Test end of chunk + if (src_p > src_mflimit) + { + src_anchor = src_p; + break; + } + + // Fill table + hash_table[(((Peek4(src, src_p - 2)) * 2654435761u) >> HASH_ADJUST)] = (src_p - 2 - src_base); + + // Test next position + + h = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (src_p - src_base); + + if ((src_ref > src_p - (MAX_DISTANCE + 1)) && (Equal4(src, src_ref, src_p))) + { + dst_token = dst_p++; + dst[dst_token] = 0; + goto _next_match; + } + + // Prepare next loop + src_anchor = src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + } + + _last_literals: + // Encode Last Literals + { + var lastRun = (src_end - src_anchor); + + if (dst_p + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > dst_end) return 0; + + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else dst[dst_p++] = (byte)(lastRun << ML_BITS); + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + } + + // End + return ((dst_p) - dst_0); + } + + #endregion + + #region LZ4_compress64kCtx + + private static int LZ4_compress64kCtx_safe32( + ushort[] hash_table, + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var debruijn32 = DEBRUIJN_TABLE_32; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_anchor = src_p; + var src_base = src_p; + var src_end = src_p + src_len; + var src_mflimit = src_end - MFLIMIT; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + + var src_LASTLITERALS = src_end - LASTLITERALS; + var src_LASTLITERALS_1 = src_LASTLITERALS - 1; + + var src_LASTLITERALS_STEPSIZE_1 = src_LASTLITERALS - (STEPSIZE_32 - 1); + var dst_LASTLITERALS_1 = dst_end - (1 + LASTLITERALS); + var dst_LASTLITERALS_3 = dst_end - (2 + 1 + LASTLITERALS); + + int len, length; + + uint h, h_fwd; + + // Init + if (src_len < MINLENGTH) goto _last_literals; + + // First Byte + src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + + // Main Loop + while (true) + { + var findMatchAttempts = (1 << SKIPSTRENGTH) + 3; + var src_p_fwd = src_p; + int src_ref; + int dst_token; + + // Find a match + do + { + h = h_fwd; + var step = findMatchAttempts++ >> SKIPSTRENGTH; + src_p = src_p_fwd; + src_p_fwd = src_p + step; + + if (src_p_fwd > src_mflimit) goto _last_literals; + + h_fwd = (((Peek4(src, src_p_fwd)) * 2654435761u) >> HASH64K_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (ushort)(src_p - src_base); + } while (!Equal4(src, src_ref, src_p)); + + // Catch up + while ((src_p > src_anchor) && (src_ref > src_0) && (src[src_p - 1] == src[src_ref - 1])) + { + src_p--; + src_ref--; + } + + // Encode Literal length + length = (src_p - src_anchor); + dst_token = dst_p++; + + if (dst_p + length + (length >> 8) > dst_LASTLITERALS_3) return 0; // Check output limit + + if (length >= RUN_MASK) + { + len = length - RUN_MASK; + dst[dst_token] = (RUN_MASK << ML_BITS); + if (len > 254) + { + do + { + dst[dst_p++] = 255; + len -= 255; + } while (len > 254); + dst[dst_p++] = (byte)len; + BlockCopy(src, src_anchor, dst, dst_p, length); + dst_p += length; + goto _next_match; + } + else + { + dst[dst_p++] = (byte)len; + } + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) + { + _i = dst_p + length; + WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + _next_match: + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Start Counting + src_p += MINMATCH; + src_ref += MINMATCH; // MinMatch verified + src_anchor = src_p; + + while (src_p < src_LASTLITERALS_STEPSIZE_1) + { + var diff = (int)Xor4(src, src_ref, src_p); + if (diff == 0) + { + src_p += STEPSIZE_32; + src_ref += STEPSIZE_32; + continue; + } + src_p += debruijn32[((uint)((diff) & -(diff)) * 0x077CB531u) >> 27]; + goto _endCount; + } + + if ((src_p < src_LASTLITERALS_1) && (Equal2(src, src_ref, src_p))) + { + src_p += 2; + src_ref += 2; + } + if ((src_p < src_LASTLITERALS) && (src[src_ref] == src[src_p])) src_p++; + + _endCount: + + // Encode MatchLength + len = (src_p - src_anchor); + + if (dst_p + (len >> 8) > dst_LASTLITERALS_1) return 0; // Check output limit + + if (len >= ML_MASK) + { + dst[dst_token] += ML_MASK; + len -= ML_MASK; + for (; len > 509; len -= 510) + { + dst[dst_p++] = 255; + dst[dst_p++] = 255; + } + if (len > 254) + { + len -= 255; + dst[dst_p++] = 255; + } + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] += (byte)len; + } + + // Test end of chunk + if (src_p > src_mflimit) + { + src_anchor = src_p; + break; + } + + // Fill table + hash_table[(((Peek4(src, src_p - 2)) * 2654435761u) >> HASH64K_ADJUST)] = (ushort)(src_p - 2 - src_base); + + // Test next position + + h = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (ushort)(src_p - src_base); + + if (Equal4(src, src_ref, src_p)) + { + dst_token = dst_p++; + dst[dst_token] = 0; + goto _next_match; + } + + // Prepare next loop + src_anchor = src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + } + + _last_literals: + // Encode Last Literals + var lastRun = (src_end - src_anchor); + if (dst_p + lastRun + 1 + (lastRun - RUN_MASK + 255) / 255 > dst_end) return 0; + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else + { + dst[dst_p++] = (byte)(lastRun << ML_BITS); + } + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + + // End + return ((dst_p) - dst_0); + } + + #endregion + + #region LZ4_uncompress + + private static int LZ4_uncompress_safe32( + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int dst_len) + { + var dec32table = DECODER_TABLE_32; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + int dst_ref; + + var dst_p = dst_0; + var dst_end = dst_p + dst_len; + int dst_cpy; + + var dst_LASTLITERALS = dst_end - LASTLITERALS; + var dst_COPYLENGTH = dst_end - COPYLENGTH; + var dst_COPYLENGTH_STEPSIZE_4 = dst_end - COPYLENGTH - (STEPSIZE_32 - 4); + + byte token; + + // Main Loop + while (true) + { + int length; + + // get runlength + token = src[src_p++]; + if ((length = (token >> ML_BITS)) == RUN_MASK) + { + int len; + for (; (len = src[src_p++]) == 255; length += 255) + { + /* do nothing */ + } + length += len; + } + + // copy literals + dst_cpy = dst_p + length; + + if (dst_cpy > dst_COPYLENGTH) + { + if (dst_cpy != dst_end) goto _output_error; // Error : not enough place for another match (min 4) + 5 literals + BlockCopy(src, src_p, dst, dst_p, length); + src_p += length; + break; // EOF + } + if (dst_p < dst_cpy) + { + _i = WildCopy(src, src_p, dst, dst_p, dst_cpy); + src_p += _i; + dst_p += _i; + } + src_p -= (dst_p - dst_cpy); + dst_p = dst_cpy; + + // get offset + dst_ref = (dst_cpy) - Peek2(src, src_p); + src_p += 2; + if (dst_ref < dst_0) goto _output_error; // Error : offset outside destination buffer + + // get matchlength + if ((length = (token & ML_MASK)) == ML_MASK) + { + for (; src[src_p] == 255; length += 255) src_p++; + length += src[src_p++]; + } + + // copy repeated sequence + if ((dst_p - dst_ref) < STEPSIZE_32) + { + const int dec64 = 0; + dst[dst_p + 0] = dst[dst_ref + 0]; + dst[dst_p + 1] = dst[dst_ref + 1]; + dst[dst_p + 2] = dst[dst_ref + 2]; + dst[dst_p + 3] = dst[dst_ref + 3]; + dst_p += 4; + dst_ref += 4; + dst_ref -= dec32table[dst_p - dst_ref]; + Copy4(dst, dst_ref, dst_p); + dst_p += STEPSIZE_32 - 4; + dst_ref -= dec64; + } + else + { + Copy4(dst, dst_ref, dst_p); + dst_p += 4; + dst_ref += 4; + } + dst_cpy = dst_p + length - (STEPSIZE_32 - 4); + + if (dst_cpy > dst_COPYLENGTH_STEPSIZE_4) + { + if (dst_cpy > dst_LASTLITERALS) goto _output_error; // Error : last 5 bytes must be literals + if (dst_p < dst_COPYLENGTH) + { + _i = SecureCopy(dst, dst_ref, dst_p, dst_COPYLENGTH); + dst_ref += _i; + dst_p += _i; + } + + while (dst_p < dst_cpy) dst[dst_p++] = dst[dst_ref++]; + dst_p = dst_cpy; + continue; + } + + if (dst_p < dst_cpy) + { + SecureCopy(dst, dst_ref, dst_p, dst_cpy); + } + dst_p = dst_cpy; // correction + } + + // end of decoding + return ((src_p) - src_0); + + // write overflow error detected + _output_error: + return (-((src_p) - src_0)); + } + + #endregion + + #region LZ4_uncompress_unknownOutputSize + + private static int LZ4_uncompress_unknownOutputSize_safe32( + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var dec32table = DECODER_TABLE_32; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_end = src_p + src_len; + int dst_ref; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + int dst_cpy; + + var src_LASTLITERALS_3 = (src_end - (2 + 1 + LASTLITERALS)); + var src_LASTLITERALS_1 = (src_end - (LASTLITERALS + 1)); + var dst_COPYLENGTH = (dst_end - COPYLENGTH); + var dst_COPYLENGTH_STEPSIZE_4 = (dst_end - (COPYLENGTH + (STEPSIZE_32 - 4))); + var dst_LASTLITERALS = (dst_end - LASTLITERALS); + var dst_MFLIMIT = (dst_end - MFLIMIT); + + // Special case + if (src_p == src_end) goto _output_error; // A correctly formed null-compressed LZ4 must have at least one byte (token=0) + + // Main Loop + while (true) + { + byte token; + int length; + + // get runlength + token = src[src_p++]; + if ((length = (token >> ML_BITS)) == RUN_MASK) + { + var s = 255; + while ((src_p < src_end) && (s == 255)) length += (s = src[src_p++]); + } + + // copy literals + dst_cpy = dst_p + length; + + if ((dst_cpy > dst_MFLIMIT) || (src_p + length > src_LASTLITERALS_3)) + { + if (dst_cpy > dst_end) goto _output_error; // Error : writes beyond output buffer + if (src_p + length != src_end) goto _output_error; // Error : LZ4 format requires to consume all input at this stage (no match within the last 11 bytes, and at least 8 remaining input bytes for another match+literals) + BlockCopy(src, src_p, dst, dst_p, length); + dst_p += length; + break; // Necessarily EOF, due to parsing restrictions + } + if (dst_p < dst_cpy) + { + _i = WildCopy(src, src_p, dst, dst_p, dst_cpy); + src_p += _i; + dst_p += _i; + } + src_p -= (dst_p - dst_cpy); + dst_p = dst_cpy; + + // get offset + dst_ref = (dst_cpy) - Peek2(src, src_p); + src_p += 2; + if (dst_ref < dst_0) goto _output_error; // Error : offset outside of destination buffer + + // get matchlength + if ((length = (token & ML_MASK)) == ML_MASK) + { + while (src_p < src_LASTLITERALS_1) // Error : a minimum input bytes must remain for LASTLITERALS + token + { + int s = src[src_p++]; + length += s; + if (s == 255) continue; + break; + } + } + + // copy repeated sequence + if (dst_p - dst_ref < STEPSIZE_32) + { + const int dec64 = 0; + dst[dst_p + 0] = dst[dst_ref + 0]; + dst[dst_p + 1] = dst[dst_ref + 1]; + dst[dst_p + 2] = dst[dst_ref + 2]; + dst[dst_p + 3] = dst[dst_ref + 3]; + dst_p += 4; + dst_ref += 4; + dst_ref -= dec32table[dst_p - dst_ref]; + Copy4(dst, dst_ref, dst_p); + dst_p += STEPSIZE_32 - 4; + dst_ref -= dec64; + } + else + { + Copy4(dst, dst_ref, dst_p); + dst_p += 4; + dst_ref += 4; + } + dst_cpy = dst_p + length - (STEPSIZE_32 - 4); + + if (dst_cpy > dst_COPYLENGTH_STEPSIZE_4) + { + if (dst_cpy > dst_LASTLITERALS) goto _output_error; // Error : last 5 bytes must be literals + if (dst_p < dst_COPYLENGTH) + { + _i = SecureCopy(dst, dst_ref, dst_p, dst_COPYLENGTH); + dst_ref += _i; + dst_p += _i; + } + + while (dst_p < dst_cpy) dst[dst_p++] = dst[dst_ref++]; + dst_p = dst_cpy; + continue; + } + + if (dst_p < dst_cpy) + { + SecureCopy(dst, dst_ref, dst_p, dst_cpy); + } + dst_p = dst_cpy; // correction + } + + // end of decoding + return ((dst_p) - dst_0); + + // write overflow error detected + _output_error: + return (-((src_p) - src_0)); + } + + #endregion + } +} + +// ReSharper restore RedundantIfElseBlock +// ReSharper restore JoinDeclarationAndInitializer +// ReSharper restore TooWideLocalVariableScope +// ReSharper restore InconsistentNaming +// ReSharper restore CheckNamespace diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs.meta new file mode 100644 index 0000000..7086673 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe32.Dirty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c5dc5c9f2a382b4db836cf150c3a176 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs b/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs new file mode 100644 index 0000000..446bbfd --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs @@ -0,0 +1,524 @@ +#region LZ4 original + +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2012, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + - LZ4 source repository : http://code.google.com/p/lz4/ +*/ + +#endregion + +#region LZ4 port + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +// ReSharper disable InconsistentNaming + +namespace LZ4ps +{ + public static partial class LZ4Codec + { + // Update chains up to ip (excluded) + private static void LZ4HC_Insert_32(LZ4HC_Data_Structure ctx, int src_p) + { + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var nextToUpdate = ctx.nextToUpdate; + var src = ctx.src; + var src_base = ctx.src_base; + + while (nextToUpdate < src_p) + { + var p = nextToUpdate; + var delta = (p) - (hashTable[(((Peek4(src, p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + if (delta > MAX_DISTANCE) delta = MAX_DISTANCE; + chainTable[(p) & MAXD_MASK] = (ushort)delta; + hashTable[(((Peek4(src, p)) * 2654435761u) >> HASHHC_ADJUST)] = ((p) - src_base); + nextToUpdate++; + } + + ctx.nextToUpdate = nextToUpdate; + } + + private static int LZ4HC_CommonLength_32(LZ4HC_Data_Structure ctx, int p1, int p2) + { + var debruijn32 = DEBRUIJN_TABLE_32; + var src = ctx.src; + var src_LASTLITERALS = ctx.src_LASTLITERALS; + + var p1t = p1; + + while (p1t < src_LASTLITERALS - (STEPSIZE_32 - 1)) + { + var diff = (int)Xor4(src, p2, p1t); + if (diff == 0) + { + p1t += STEPSIZE_32; + p2 += STEPSIZE_32; + continue; + } + p1t += debruijn32[((uint)((diff) & -(diff)) * 0x077CB531u) >> 27]; + return (p1t - p1); + } + if ((p1t < (src_LASTLITERALS - 1)) && (Equal2(src, p2, p1t))) + { + p1t += 2; + p2 += 2; + } + if ((p1t < src_LASTLITERALS) && (src[p2] == src[p1t])) p1t++; + return (p1t - p1); + } + + private static int LZ4HC_InsertAndFindBestMatch_32(LZ4HC_Data_Structure ctx, int src_p, ref int src_match) + { + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var src = ctx.src; + var src_base = ctx.src_base; + + var nbAttempts = MAX_NB_ATTEMPTS; + int repl = 0, ml = 0; + ushort delta = 0; + + // HC4 match finder + LZ4HC_Insert_32(ctx, src_p); + var src_ref = (hashTable[(((Peek4(src, src_p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + + + // Detect repetitive sequences of length <= 4 + if (src_ref >= src_p - 4) // potential repetition + { + if (Equal4(src, src_ref, src_p)) // confirmed + { + delta = (ushort)(src_p - src_ref); + repl = ml = LZ4HC_CommonLength_32(ctx, src_p + MINMATCH, src_ref + MINMATCH) + MINMATCH; + src_match = src_ref; + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + while ((src_ref >= src_p - MAX_DISTANCE) && (nbAttempts != 0)) + { + nbAttempts--; + if (src[(src_ref + ml)] == src[(src_p + ml)]) + { + if (Equal4(src, src_ref, src_p)) + { + var mlt = LZ4HC_CommonLength_32(ctx, src_p + MINMATCH, src_ref + MINMATCH) + MINMATCH; + if (mlt > ml) + { + ml = mlt; + src_match = src_ref; + } + } + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + + // Complete table + if (repl != 0) + { + var src_ptr = src_p; + + var end = src_p + repl - (MINMATCH - 1); + while (src_ptr < end - delta) + { + chainTable[(src_ptr) & MAXD_MASK] = delta; // Pre-Load + src_ptr++; + } + do + { + chainTable[(src_ptr) & MAXD_MASK] = delta; + hashTable[(((Peek4(src, src_ptr)) * 2654435761u) >> HASHHC_ADJUST)] = ((src_ptr) - src_base); // Head of chain + src_ptr++; + } while (src_ptr < end); + ctx.nextToUpdate = end; + } + + return ml; + } + + private static int LZ4HC_InsertAndGetWiderMatch_32( + LZ4HC_Data_Structure ctx, int src_p, int startLimit, int longest, ref int matchpos, ref int startpos) + { + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var src = ctx.src; + var src_base = ctx.src_base; + var src_LASTLITERALS = ctx.src_LASTLITERALS; + var debruijn32 = DEBRUIJN_TABLE_32; + var nbAttempts = MAX_NB_ATTEMPTS; + var delta = (src_p - startLimit); + + // First Match + LZ4HC_Insert_32(ctx, src_p); + var src_ref = (hashTable[(((Peek4(src, src_p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + + while ((src_ref >= src_p - MAX_DISTANCE) && (nbAttempts != 0)) + { + nbAttempts--; + if (src[(startLimit + longest)] == src[(src_ref - delta + longest)]) + { + if (Equal4(src, src_ref, src_p)) + { + var reft = src_ref + MINMATCH; + var ipt = src_p + MINMATCH; + var startt = src_p; + + while (ipt < src_LASTLITERALS - (STEPSIZE_32 - 1)) + { + var diff = (int)Xor4(src, reft, ipt); + if (diff == 0) + { + ipt += STEPSIZE_32; + reft += STEPSIZE_32; + continue; + } + ipt += debruijn32[((uint)((diff) & -(diff)) * 0x077CB531u) >> 27]; + goto _endCount; + } + if ((ipt < (src_LASTLITERALS - 1)) && (Equal2(src, reft, ipt))) + { + ipt += 2; + reft += 2; + } + if ((ipt < src_LASTLITERALS) && (src[reft] == src[ipt])) ipt++; + + _endCount: + reft = src_ref; + + while ((startt > startLimit) && (reft > src_base) && (src[startt - 1] == src[reft - 1])) + { + startt--; + reft--; + } + + if ((ipt - startt) > longest) + { + longest = (ipt - startt); + matchpos = reft; + startpos = startt; + } + } + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + return longest; + } + + private static int LZ4_encodeSequence_32( + LZ4HC_Data_Structure ctx, ref int src_p, ref int dst_p, ref int src_anchor, int matchLength, int src_ref, int dst_end) + { + int len; + var src = ctx.src; + var dst = ctx.dst; + + // Encode Literal length + var length = src_p - src_anchor; + var dst_token = dst_p++; + if ((dst_p + length + (2 + 1 + LASTLITERALS) + (length >> 8)) > dst_end) return 1; // Check output limit + if (length >= RUN_MASK) + { + dst[dst_token] = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254; len -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) + { + var _i = dst_p + length; + src_anchor += WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Encode MatchLength + len = (matchLength - MINMATCH); + if (dst_p + (1 + LASTLITERALS) + (length >> 8) > dst_end) return 1; // Check output limit + if (len >= ML_MASK) + { + dst[dst_token] += ML_MASK; + len -= ML_MASK; + for (; len > 509; len -= 510) + { + dst[(dst_p)++] = 255; + dst[(dst_p)++] = 255; + } + if (len > 254) + { + len -= 255; + dst[(dst_p)++] = 255; + } + dst[(dst_p)++] = (byte)len; + } + else + { + dst[dst_token] += (byte)len; + } + + // Prepare next loop + src_p += matchLength; + src_anchor = src_p; + + return 0; + } + + private static int LZ4_compressHCCtx_32(LZ4HC_Data_Structure ctx) + { + var src = ctx.src; + var dst = ctx.dst; + var src_0 = ctx.src_base; + var src_end = ctx.src_end; + var dst_0 = ctx.dst_base; + var dst_len = ctx.dst_len; + var dst_end = ctx.dst_end; + + var src_p = src_0; + var src_anchor = src_p; + var src_mflimit = src_end - MFLIMIT; + + var dst_p = dst_0; + + var src_ref = 0; + var start2 = 0; + var ref2 = 0; + var start3 = 0; + var ref3 = 0; + + src_p++; + + // Main Loop + while (src_p < src_mflimit) + { + var ml = LZ4HC_InsertAndFindBestMatch_32(ctx, src_p, ref src_ref); + if (ml == 0) + { + src_p++; + continue; + } + + // saved, in case we would skip too much + var start0 = src_p; + var ref0 = src_ref; + var ml0 = ml; + + _Search2: + var ml2 = src_p + ml < src_mflimit + ? LZ4HC_InsertAndGetWiderMatch_32(ctx, src_p + ml - 2, src_p + 1, ml, ref ref2, ref start2) + : ml; + + if (ml2 == ml) // No better match + { + if (LZ4_encodeSequence_32(ctx, ref src_p, ref dst_p, ref src_anchor, ml, src_ref, dst_end) != 0) return 0; + continue; + } + + if (start0 < src_p) + { + if (start2 < src_p + ml0) // empirical + { + src_p = start0; + src_ref = ref0; + ml = ml0; + } + } + + // Here, start0==ip + if ((start2 - src_p) < 3) // First Match too small : removed + { + ml = ml2; + src_p = start2; + src_ref = ref2; + goto _Search2; + } + + _Search3: + // Currently we have : + // ml2 > ml1, and + // ip1+3 <= ip2 (usually < ip1+ml1) + if ((start2 - src_p) < OPTIMAL_ML) + { + var new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (src_p + new_ml > start2 + ml2 - MINMATCH) new_ml = (start2 - src_p) + ml2 - MINMATCH; + var correction = new_ml - (start2 - src_p); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + // Now, we have start2 = ip+new_ml, with new_ml=min(ml, OPTIMAL_ML=18) + + var ml3 = start2 + ml2 < src_mflimit + ? LZ4HC_InsertAndGetWiderMatch_32(ctx, start2 + ml2 - 3, start2, ml2, ref ref3, ref start3) + : ml2; + + if (ml3 == ml2) // No better match : 2 sequences to encode + { + // ip & ref are known; Now for ml + if (start2 < src_p + ml) ml = (start2 - src_p); + // Now, encode 2 sequences + if (LZ4_encodeSequence_32(ctx, ref src_p, ref dst_p, ref src_anchor, ml, src_ref, dst_end) != 0) return 0; + src_p = start2; + if (LZ4_encodeSequence_32(ctx, ref src_p, ref dst_p, ref src_anchor, ml2, ref2, dst_end) != 0) return 0; + continue; + } + + if (start3 < src_p + ml + 3) // Not enough space for match 2 : remove it + { + if (start3 >= (src_p + ml)) // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 + { + if (start2 < src_p + ml) + { + var correction = (src_p + ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) + { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + if (LZ4_encodeSequence_32(ctx, ref src_p, ref dst_p, ref src_anchor, ml, src_ref, dst_end) != 0) return 0; + src_p = start3; + src_ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + // OK, now we have 3 ascending matches; let's write at least the first one + // ip & ref are known; Now for ml + if (start2 < src_p + ml) + { + if ((start2 - src_p) < ML_MASK) + { + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (src_p + ml > start2 + ml2 - MINMATCH) ml = (start2 - src_p) + ml2 - MINMATCH; + var correction = ml - (start2 - src_p); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + else + { + ml = (start2 - src_p); + } + } + if (LZ4_encodeSequence_32(ctx, ref src_p, ref dst_p, ref src_anchor, ml, src_ref, dst_end) != 0) return 0; + + src_p = start2; + src_ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _Search3; + } + + // Encode Last Literals + { + var lastRun = (src_end - src_anchor); + if ((dst_p - dst_0) + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > (uint)dst_len) return 0; // Check output limit + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else + { + dst[dst_p++] = (byte)(lastRun << ML_BITS); + } + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + } + + // End + return (dst_p - dst_0); + } + } +} + +// ReSharper restore InconsistentNaming diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs.meta new file mode 100644 index 0000000..f1a35b3 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe32HC.Dirty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cd0b555f5386b84b9fc6aea0b43418d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs b/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs new file mode 100644 index 0000000..fbeee17 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs @@ -0,0 +1,808 @@ +#region LZ4 original + +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2012, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + - LZ4 source repository : http://code.google.com/p/lz4/ +*/ + +#endregion + +#region LZ4 port + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable TooWideLocalVariableScope +// ReSharper disable JoinDeclarationAndInitializer +// ReSharper disable RedundantIfElseBlock + +namespace LZ4ps +{ + public static partial class LZ4Codec + { + #region LZ4_compressCtx + + private static int LZ4_compressCtx_safe64( + int[] hash_table, + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var debruijn64 = DEBRUIJN_TABLE_64; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_base = src_0; + var src_anchor = src_p; + var src_end = src_p + src_len; + var src_mflimit = src_end - MFLIMIT; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + + var src_LASTLITERALS = src_end - LASTLITERALS; + var src_LASTLITERALS_1 = src_LASTLITERALS - 1; + + var src_LASTLITERALS_3 = src_LASTLITERALS - 3; + + var src_LASTLITERALS_STEPSIZE_1 = src_LASTLITERALS - (STEPSIZE_64 - 1); + var dst_LASTLITERALS_1 = dst_end - (1 + LASTLITERALS); + var dst_LASTLITERALS_3 = dst_end - (2 + 1 + LASTLITERALS); + + int length; + + uint h, h_fwd; + + // Init + if (src_len < MINLENGTH) goto _last_literals; + + // First Byte + hash_table[(((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST)] = (src_p - src_base); + src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + + // Main Loop + while (true) + { + var findMatchAttempts = (1 << SKIPSTRENGTH) + 3; + var src_p_fwd = src_p; + int src_ref; + int dst_token; + + // Find a match + do + { + h = h_fwd; + var step = findMatchAttempts++ >> SKIPSTRENGTH; + src_p = src_p_fwd; + src_p_fwd = src_p + step; + + if (src_p_fwd > src_mflimit) goto _last_literals; + + h_fwd = (((Peek4(src, src_p_fwd)) * 2654435761u) >> HASH_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (src_p - src_base); + } while ((src_ref < src_p - MAX_DISTANCE) || (!Equal4(src, src_ref, src_p))); + + // Catch up + while ((src_p > src_anchor) && (src_ref > src_0) && (src[src_p - 1] == src[src_ref - 1])) + { + src_p--; + src_ref--; + } + + // Encode Literal length + length = (src_p - src_anchor); + dst_token = dst_p++; + + if (dst_p + length + (length >> 8) > dst_LASTLITERALS_3) return 0; // Check output limit + + if (length >= RUN_MASK) + { + var len = length - RUN_MASK; + dst[dst_token] = (RUN_MASK << ML_BITS); + if (len > 254) + { + do + { + dst[dst_p++] = 255; + len -= 255; + } while (len > 254); + dst[dst_p++] = (byte)len; + BlockCopy(src, src_anchor, dst, dst_p, length); + dst_p += length; + goto _next_match; + } + else + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) + { + _i = dst_p + length; + WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + _next_match: + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Start Counting + src_p += MINMATCH; + src_ref += MINMATCH; // MinMatch already verified + src_anchor = src_p; + + while (src_p < src_LASTLITERALS_STEPSIZE_1) + { + var diff = (long)Xor8(src, src_ref, src_p); + if (diff == 0) + { + src_p += STEPSIZE_64; + src_ref += STEPSIZE_64; + continue; + } + src_p += debruijn64[((ulong)((diff) & -(diff)) * 0x0218A392CDABBD3FL) >> 58]; + goto _endCount; + } + + if ((src_p < src_LASTLITERALS_3) && (Equal4(src, src_ref, src_p))) + { + src_p += 4; + src_ref += 4; + } + if ((src_p < src_LASTLITERALS_1) && (Equal2(src, src_ref, src_p))) + { + src_p += 2; + src_ref += 2; + } + if ((src_p < src_LASTLITERALS) && (src[src_ref] == src[src_p])) src_p++; + + _endCount: + // Encode MatchLength + length = (src_p - src_anchor); + + if (dst_p + (length >> 8) > dst_LASTLITERALS_1) return 0; // Check output limit + + if (length >= ML_MASK) + { + dst[dst_token] += ML_MASK; + length -= ML_MASK; + for (; length > 509; length -= 510) + { + dst[dst_p++] = 255; + dst[dst_p++] = 255; + } + if (length > 254) + { + length -= 255; + dst[dst_p++] = 255; + } + dst[dst_p++] = (byte)length; + } + else + { + dst[dst_token] += (byte)length; + } + + // Test end of chunk + if (src_p > src_mflimit) + { + src_anchor = src_p; + break; + } + + // Fill table + hash_table[(((Peek4(src, src_p - 2)) * 2654435761u) >> HASH_ADJUST)] = (src_p - 2 - src_base); + + // Test next position + + h = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (src_p - src_base); + + if ((src_ref > src_p - (MAX_DISTANCE + 1)) && (Equal4(src, src_ref, src_p))) + { + dst_token = dst_p++; + dst[dst_token] = 0; + goto _next_match; + } + + // Prepare next loop + src_anchor = src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH_ADJUST); + } + + _last_literals: + // Encode Last Literals + { + var lastRun = (src_end - src_anchor); + + if (dst_p + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > dst_end) return 0; + + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else dst[dst_p++] = (byte)(lastRun << ML_BITS); + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + } + + // End + return (dst_p - dst_0); + } + + #endregion + + #region LZ4_compress64kCtx + + private static int LZ4_compress64kCtx_safe64( + ushort[] hash_table, + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var debruijn64 = DEBRUIJN_TABLE_64; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_anchor = src_p; + var src_base = src_p; + var src_end = src_p + src_len; + var src_mflimit = src_end - MFLIMIT; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + + var src_LASTLITERALS = src_end - LASTLITERALS; + var src_LASTLITERALS_1 = src_LASTLITERALS - 1; + + var src_LASTLITERALS_3 = src_LASTLITERALS - 3; + + var src_LASTLITERALS_STEPSIZE_1 = src_LASTLITERALS - (STEPSIZE_64 - 1); + var dst_LASTLITERALS_1 = dst_end - (1 + LASTLITERALS); + var dst_LASTLITERALS_3 = dst_end - (2 + 1 + LASTLITERALS); + + int len, length; + + uint h, h_fwd; + + // Init + if (src_len < MINLENGTH) goto _last_literals; + + // First Byte + src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + + // Main Loop + while (true) + { + var findMatchAttempts = (1 << SKIPSTRENGTH) + 3; + var src_p_fwd = src_p; + int src_ref; + int dst_token; + + // Find a match + do + { + h = h_fwd; + var step = findMatchAttempts++ >> SKIPSTRENGTH; + src_p = src_p_fwd; + src_p_fwd = src_p + step; + + if (src_p_fwd > src_mflimit) goto _last_literals; + + h_fwd = (((Peek4(src, src_p_fwd)) * 2654435761u) >> HASH64K_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (ushort)(src_p - src_base); + } while (!Equal4(src, src_ref, src_p)); + + // Catch up + while ((src_p > src_anchor) && (src_ref > src_0) && (src[src_p - 1] == src[src_ref - 1])) + { + src_p--; + src_ref--; + } + + // Encode Literal length + length = (src_p - src_anchor); + dst_token = dst_p++; + + if (dst_p + length + (length >> 8) > dst_LASTLITERALS_3) return 0; // Check output limit + + if (length >= RUN_MASK) + { + len = length - RUN_MASK; + dst[dst_token] = (RUN_MASK << ML_BITS); + if (len > 254) + { + do + { + dst[dst_p++] = 255; + len -= 255; + } while (len > 254); + dst[dst_p++] = (byte)len; + BlockCopy(src, src_anchor, dst, dst_p, length); + dst_p += length; + goto _next_match; + } + else + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) /*?*/ + { + _i = dst_p + length; + WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + _next_match: + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Start Counting + src_p += MINMATCH; + src_ref += MINMATCH; // MinMatch verified + src_anchor = src_p; + + while (src_p < src_LASTLITERALS_STEPSIZE_1) + { + var diff = (long)Xor8(src, src_ref, src_p); + if (diff == 0) + { + src_p += STEPSIZE_64; + src_ref += STEPSIZE_64; + continue; + } + src_p += debruijn64[((ulong)((diff) & -(diff)) * 0x0218A392CDABBD3FL) >> 58]; + goto _endCount; + } + + if ((src_p < src_LASTLITERALS_3) && (Equal4(src, src_ref, src_p))) + { + src_p += 4; + src_ref += 4; + } + if ((src_p < src_LASTLITERALS_1) && (Equal2(src, src_ref, src_p))) + { + src_p += 2; + src_ref += 2; + } + if ((src_p < src_LASTLITERALS) && (src[src_ref] == src[src_p])) src_p++; + + _endCount: + + // Encode MatchLength + len = (src_p - src_anchor); + + if (dst_p + (len >> 8) > dst_LASTLITERALS_1) return 0; // Check output limit + + if (len >= ML_MASK) + { + dst[dst_token] += ML_MASK; + len -= ML_MASK; + for (; len > 509; len -= 510) + { + dst[dst_p++] = 255; + dst[dst_p++] = 255; + } + if (len > 254) + { + len -= 255; + dst[dst_p++] = 255; + } + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] += (byte)len; + } + + // Test end of chunk + if (src_p > src_mflimit) + { + src_anchor = src_p; + break; + } + + // Fill table + hash_table[(((Peek4(src, src_p - 2)) * 2654435761u) >> HASH64K_ADJUST)] = (ushort)(src_p - 2 - src_base); + + // Test next position + + h = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + src_ref = src_base + hash_table[h]; + hash_table[h] = (ushort)(src_p - src_base); + + if (Equal4(src, src_ref, src_p)) + { + dst_token = dst_p++; + dst[dst_token] = 0; + goto _next_match; + } + + // Prepare next loop + src_anchor = src_p++; + h_fwd = (((Peek4(src, src_p)) * 2654435761u) >> HASH64K_ADJUST); + } + + _last_literals: + // Encode Last Literals + { + var lastRun = (src_end - src_anchor); + if (dst_p + lastRun + 1 + (lastRun - RUN_MASK + 255) / 255 > dst_end) return 0; + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else dst[dst_p++] = (byte)(lastRun << ML_BITS); + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + } + + // End + return (dst_p - dst_0); + } + + #endregion + + #region LZ4_uncompress + + private static int LZ4_uncompress_safe64( + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int dst_len) + { + var dec32table = DECODER_TABLE_32; + var dec64table = DECODER_TABLE_64; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + int dst_ref; + + var dst_p = dst_0; + var dst_end = dst_p + dst_len; + int dst_cpy; + + var dst_LASTLITERALS = dst_end - LASTLITERALS; + var dst_COPYLENGTH = dst_end - COPYLENGTH; + var dst_COPYLENGTH_STEPSIZE_4 = dst_end - COPYLENGTH - (STEPSIZE_64 - 4); + + uint token; + + // Main Loop + while (true) + { + int length; + + // get runlength + token = src[src_p++]; + if ((length = (byte)(token >> ML_BITS)) == RUN_MASK) + { + int len; + for (; (len = src[src_p++]) == 255; length += 255) + { + /* do nothing */ + } + length += len; + } + + // copy literals + dst_cpy = dst_p + length; + + if (dst_cpy > dst_COPYLENGTH) + { + if (dst_cpy != dst_end) goto _output_error; // Error : not enough place for another match (min 4) + 5 literals + BlockCopy(src, src_p, dst, dst_p, length); + src_p += length; + break; // EOF + } + if (dst_p < dst_cpy) /*?*/ + { + _i = WildCopy(src, src_p, dst, dst_p, dst_cpy); + src_p += _i; + dst_p += _i; + } + src_p -= (dst_p - dst_cpy); + dst_p = dst_cpy; + + // get offset + dst_ref = (dst_cpy) - Peek2(src, src_p); + src_p += 2; + if (dst_ref < dst_0) goto _output_error; // Error : offset outside destination buffer + + // get matchlength + if ((length = (byte)(token & ML_MASK)) == ML_MASK) + { + for (; src[src_p] == 255; length += 255) src_p++; + length += src[src_p++]; + } + + // copy repeated sequence + if ((dst_p - dst_ref) < STEPSIZE_64) + { + var dec64 = dec64table[dst_p - dst_ref]; + + dst[dst_p + 0] = dst[dst_ref + 0]; + dst[dst_p + 1] = dst[dst_ref + 1]; + dst[dst_p + 2] = dst[dst_ref + 2]; + dst[dst_p + 3] = dst[dst_ref + 3]; + dst_p += 4; + dst_ref += 4; + dst_ref -= dec32table[dst_p - dst_ref]; + Copy4(dst, dst_ref, dst_p); + dst_p += STEPSIZE_64 - 4; + dst_ref -= dec64; + } + else + { + Copy8(dst, dst_ref, dst_p); + dst_p += 8; + dst_ref += 8; + } + dst_cpy = dst_p + length - (STEPSIZE_64 - 4); + + if (dst_cpy > dst_COPYLENGTH_STEPSIZE_4) + { + if (dst_cpy > dst_LASTLITERALS) goto _output_error; // Error : last 5 bytes must be literals + if (dst_p < dst_COPYLENGTH) + { + _i = SecureCopy(dst, dst_ref, dst_p, dst_COPYLENGTH); + dst_ref += _i; + dst_p += _i; + } + + while (dst_p < dst_cpy) dst[dst_p++] = dst[dst_ref++]; + dst_p = dst_cpy; + continue; + } + + if (dst_p < dst_cpy) + { + SecureCopy(dst, dst_ref, dst_p, dst_cpy); + } + dst_p = dst_cpy; // correction + } + + // end of decoding + return ((src_p) - src_0); + + _output_error: + // write overflow error detected + return (-((src_p) - src_0)); + } + + #endregion + + #region LZ4_uncompress_unknownOutputSize + + private static int LZ4_uncompress_unknownOutputSize_safe64( + byte[] src, + byte[] dst, + int src_0, + int dst_0, + int src_len, + int dst_maxlen) + { + var dec32table = DECODER_TABLE_32; + var dec64table = DECODER_TABLE_64; + int _i; + + // ---- preprocessed source start here ---- + // r93 + var src_p = src_0; + var src_end = src_p + src_len; + int dst_ref; + + var dst_p = dst_0; + var dst_end = dst_p + dst_maxlen; + int dst_cpy; + + var src_LASTLITERALS_3 = (src_end - (2 + 1 + LASTLITERALS)); + var src_LASTLITERALS_1 = (src_end - (LASTLITERALS + 1)); + var dst_COPYLENGTH = (dst_end - COPYLENGTH); + var dst_COPYLENGTH_STEPSIZE_4 = (dst_end - (COPYLENGTH + (STEPSIZE_64 - 4))); + var dst_LASTLITERALS = (dst_end - LASTLITERALS); + var dst_MFLIMIT = (dst_end - MFLIMIT); + + // Special case + if (src_p == src_end) goto _output_error; // A correctly formed null-compressed LZ4 must have at least one byte (token=0) + + // Main Loop + while (true) + { + byte token; + int length; + + // get runlength + token = src[src_p++]; + if ((length = (token >> ML_BITS)) == RUN_MASK) + { + var s = 255; + while ((src_p < src_end) && (s == 255)) length += (s = src[src_p++]); + } + + // copy literals + dst_cpy = dst_p + length; + + if ((dst_cpy > dst_MFLIMIT) || (src_p + length > src_LASTLITERALS_3)) + { + if (dst_cpy > dst_end) goto _output_error; // Error : writes beyond output buffer + if (src_p + length != src_end) goto _output_error; // Error : LZ4 format requires to consume all input at this stage (no match within the last 11 bytes, and at least 8 remaining input bytes for another match+literals) + BlockCopy(src, src_p, dst, dst_p, length); + dst_p += length; + break; // Necessarily EOF, due to parsing restrictions + } + if (dst_p < dst_cpy) /*?*/ + { + _i = WildCopy(src, src_p, dst, dst_p, dst_cpy); + src_p += _i; + dst_p += _i; + } + src_p -= (dst_p - dst_cpy); + dst_p = dst_cpy; + + // get offset + dst_ref = (dst_cpy) - Peek2(src, src_p); + src_p += 2; + if (dst_ref < dst_0) goto _output_error; // Error : offset outside of destination buffer + + // get matchlength + if ((length = (token & ML_MASK)) == ML_MASK) + { + while (src_p < src_LASTLITERALS_1) // Error : a minimum input bytes must remain for LASTLITERALS + token + { + int s = src[src_p++]; + length += s; + if (s == 255) continue; + break; + } + } + + // copy repeated sequence + if (dst_p - dst_ref < STEPSIZE_64) + { + var dec64 = dec64table[dst_p - dst_ref]; + dst[dst_p + 0] = dst[dst_ref + 0]; + dst[dst_p + 1] = dst[dst_ref + 1]; + dst[dst_p + 2] = dst[dst_ref + 2]; + dst[dst_p + 3] = dst[dst_ref + 3]; + dst_p += 4; + dst_ref += 4; + dst_ref -= dec32table[dst_p - dst_ref]; + Copy4(dst, dst_ref, dst_p); + dst_p += STEPSIZE_64 - 4; + dst_ref -= dec64; + } + else + { + Copy8(dst, dst_ref, dst_p); + dst_p += 8; + dst_ref += 8; + } + dst_cpy = dst_p + length - (STEPSIZE_64 - 4); + + if (dst_cpy > dst_COPYLENGTH_STEPSIZE_4) + { + if (dst_cpy > dst_LASTLITERALS) goto _output_error; // Error : last 5 bytes must be literals + if (dst_p < dst_COPYLENGTH) + { + _i = SecureCopy(dst, dst_ref, dst_p, dst_COPYLENGTH); + dst_ref += _i; + dst_p += _i; + } + while (dst_p < dst_cpy) dst[dst_p++] = dst[dst_ref++]; + dst_p = dst_cpy; + continue; + } + + if (dst_p < dst_cpy) + { + SecureCopy(dst, dst_ref, dst_p, dst_cpy); + } + dst_p = dst_cpy; // correction + } + + // end of decoding + return ((dst_p) - dst_0); + + // write overflow error detected + _output_error: + return (-((src_p) - src_0)); + } + + #endregion + } +} + +// ReSharper restore RedundantIfElseBlock +// ReSharper restore JoinDeclarationAndInitializer +// ReSharper restore TooWideLocalVariableScope +// ReSharper restore InconsistentNaming +// ReSharper restore CheckNamespace diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs.meta new file mode 100644 index 0000000..bb791e8 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe64.Dirty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04fd67d662c762548a89d1fe5bf6cfcc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs b/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs new file mode 100644 index 0000000..8ff946c --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs @@ -0,0 +1,526 @@ +#region LZ4 original + +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2012, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + - LZ4 source repository : http://code.google.com/p/lz4/ +*/ + +#endregion + +#region LZ4 port + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +// ReSharper disable InconsistentNaming + +namespace LZ4ps +{ + public static partial class LZ4Codec + { + private static void LZ4HC_Insert_64(LZ4HC_Data_Structure ctx, int src_p) + { + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var src = ctx.src; + var src_base = ctx.src_base; + var nextToUpdate = ctx.nextToUpdate; + + while (nextToUpdate < src_p) + { + var p = nextToUpdate; + var delta = (p) - (hashTable[(((Peek4(src, p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + if (delta > MAX_DISTANCE) delta = MAX_DISTANCE; + chainTable[(p) & MAXD_MASK] = (ushort)delta; + hashTable[(((Peek4(src, p)) * 2654435761u) >> HASHHC_ADJUST)] = ((p) - src_base); + nextToUpdate++; + } + + ctx.nextToUpdate = nextToUpdate; + } + + private static int LZ4HC_CommonLength_64(LZ4HC_Data_Structure ctx, int p1, int p2) + { + var debruijn64 = DEBRUIJN_TABLE_64; + var src = ctx.src; + var src_LASTLITERALS = ctx.src_LASTLITERALS; + var p1t = p1; + + while (p1t < src_LASTLITERALS - (STEPSIZE_64 - 1)) + { + var diff = (long)Xor8(src, p2, p1t); + if (diff == 0) + { + p1t += STEPSIZE_64; + p2 += STEPSIZE_64; + continue; + } + p1t += debruijn64[((ulong)((diff) & -(diff)) * 0x0218A392CDABBD3FL) >> 58]; + return (p1t - p1); + } + if ((p1t < (src_LASTLITERALS - 3)) && (Equal4(src, p2, p1t))) + { + p1t += 4; + p2 += 4; + } + if ((p1t < (src_LASTLITERALS - 1)) && (Equal2(src, p2, p1t))) + { + p1t += 2; + p2 += 2; + } + if ((p1t < src_LASTLITERALS) && (src[p2] == src[p1t])) p1t++; + return (p1t - p1); + } + + private static int LZ4HC_InsertAndFindBestMatch_64(LZ4HC_Data_Structure ctx, int src_p, ref int matchpos) + { + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var src = ctx.src; + var src_base = ctx.src_base; + var nbAttempts = MAX_NB_ATTEMPTS; + int repl = 0, ml = 0; + ushort delta = 0; + + // HC4 match finder + LZ4HC_Insert_64(ctx, src_p); + var src_ref = (hashTable[(((Peek4(src, src_p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + + // Detect repetitive sequences of length <= 4 + if (src_ref >= src_p - 4) // potential repetition + { + if (Equal4(src, src_ref, src_p)) // confirmed + { + delta = (ushort)(src_p - src_ref); + repl = ml = LZ4HC_CommonLength_64(ctx, src_p + MINMATCH, src_ref + MINMATCH) + MINMATCH; + matchpos = src_ref; + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + + while ((src_ref >= src_p - MAX_DISTANCE) && (nbAttempts != 0)) + { + nbAttempts--; + if (src[src_ref + ml] == src[src_p + ml]) + { + if (Equal4(src, src_ref, src_p)) + { + var mlt = LZ4HC_CommonLength_64(ctx, src_p + MINMATCH, src_ref + MINMATCH) + MINMATCH; + if (mlt > ml) + { + ml = mlt; + matchpos = src_ref; + } + } + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + + // Complete table + if (repl != 0) + { + var ptr = src_p; + + var end = src_p + repl - (MINMATCH - 1); + while (ptr < end - delta) + { + chainTable[(ptr) & MAXD_MASK] = delta; // Pre-Load + ptr++; + } + do + { + chainTable[(ptr) & MAXD_MASK] = delta; + hashTable[(((Peek4(src, ptr)) * 2654435761u) >> HASHHC_ADJUST)] = ((ptr) - src_base); // Head of chain + ptr++; + } while (ptr < end); + ctx.nextToUpdate = end; + } + + return ml; + } + + private static int LZ4HC_InsertAndGetWiderMatch_64(LZ4HC_Data_Structure ctx, int src_p, int startLimit, int longest, ref int matchpos, ref int startpos) + { + var debruijn64 = DEBRUIJN_TABLE_64; + var chainTable = ctx.chainTable; + var hashTable = ctx.hashTable; + var src = ctx.src; + var src_base = ctx.src_base; + var src_LASTLITERALS = ctx.src_LASTLITERALS; + var nbAttempts = MAX_NB_ATTEMPTS; + var delta = (src_p - startLimit); + + // First Match + LZ4HC_Insert_64(ctx, src_p); + var src_ref = (hashTable[(((Peek4(src, src_p)) * 2654435761u) >> HASHHC_ADJUST)] + src_base); + + while ((src_ref >= src_p - MAX_DISTANCE) && (nbAttempts != 0)) + { + nbAttempts--; + if (src[startLimit + longest] == src[src_ref - delta + longest]) + { + if (Equal4(src, src_ref, src_p)) + { + var reft = src_ref + MINMATCH; + var ipt = src_p + MINMATCH; + var startt = src_p; + + while (ipt < src_LASTLITERALS - (STEPSIZE_64 - 1)) + { + var diff = (long)Xor8(src, reft, ipt); + if (diff == 0) + { + ipt += STEPSIZE_64; + reft += STEPSIZE_64; + continue; + } + ipt += debruijn64[((ulong)((diff) & -(diff)) * 0x0218A392CDABBD3FL) >> 58]; + goto _endCount; + } + if ((ipt < (src_LASTLITERALS - 3)) && (Equal4(src, reft, ipt))) + { + ipt += 4; + reft += 4; + } + if ((ipt < (src_LASTLITERALS - 1)) && (Equal2(src, reft, ipt))) + { + ipt += 2; + reft += 2; + } + if ((ipt < src_LASTLITERALS) && (src[reft] == src[ipt])) ipt++; + + _endCount: + reft = src_ref; + + while ((startt > startLimit) && (reft > src_base) && (src[startt - 1] == src[reft - 1])) + { + startt--; + reft--; + } + + if ((ipt - startt) > longest) + { + longest = (ipt - startt); + matchpos = reft; + startpos = startt; + } + } + } + src_ref = ((src_ref) - chainTable[(src_ref) & MAXD_MASK]); + } + + return longest; + } + + private static int LZ4_encodeSequence_64(LZ4HC_Data_Structure ctx, ref int src_p, ref int dst_p, ref int src_anchor, int matchLength, int src_ref) + { + var src = ctx.src; + var dst = ctx.dst; + var dst_end = ctx.dst_end; + int len; + + // Encode Literal length + var length = (src_p - src_anchor); + var dst_token = (dst_p)++; + if ((dst_p + length + (2 + 1 + LASTLITERALS) + (length >> 8)) > dst_end) return 1; // Check output limit + if (length >= RUN_MASK) + { + dst[dst_token] = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254; len -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] = (byte)(length << ML_BITS); + } + + // Copy Literals + if (length > 0) + { + var _i = dst_p + length; + src_anchor += WildCopy(src, src_anchor, dst, dst_p, _i); + dst_p = _i; + } + + // Encode Offset + Poke2(dst, dst_p, (ushort)(src_p - src_ref)); + dst_p += 2; + + // Encode MatchLength + len = (matchLength - MINMATCH); + if (dst_p + (1 + LASTLITERALS) + (length >> 8) > dst_end) return 1; // Check output limit + if (len >= ML_MASK) + { + dst[dst_token] += ML_MASK; + len -= ML_MASK; + for (; len > 509; len -= 510) + { + dst[dst_p++] = 255; + dst[dst_p++] = 255; + } + if (len > 254) + { + len -= 255; + dst[dst_p++] = 255; + } + dst[dst_p++] = (byte)len; + } + else + { + dst[dst_token] += (byte)len; + } + + // Prepare next loop + src_p += matchLength; + src_anchor = src_p; + + return 0; + } + + private static int LZ4_compressHCCtx_64(LZ4HC_Data_Structure ctx) + { + var src = ctx.src; + var src_p = ctx.src_base; + var src_end = ctx.src_end; + var dst_0 = ctx.dst_base; + var src_anchor = src_p; + var src_mflimit = src_end - MFLIMIT; + var dst = ctx.dst; + var dst_len = ctx.dst_len; + var dst_p = ctx.dst_base; + + var xxx_ref = 0; + var start2 = 0; + var ref2 = 0; + var start3 = 0; + var ref3 = 0; + + src_p++; + + // Main Loop + while (src_p < src_mflimit) + { + var ml = LZ4HC_InsertAndFindBestMatch_64(ctx, src_p, ref xxx_ref); + if (ml == 0) + { + src_p++; + continue; + } + + // saved, in case we would skip too much + var start0 = src_p; + var ref0 = xxx_ref; + var ml0 = ml; + + _Search2: + var ml2 = src_p + ml < src_mflimit + ? LZ4HC_InsertAndGetWiderMatch_64(ctx, src_p + ml - 2, src_p + 1, ml, ref ref2, ref start2) + : ml; + + if (ml2 == ml) // No better match + { + if (LZ4_encodeSequence_64(ctx, ref src_p, ref dst_p, ref src_anchor, ml, xxx_ref) != 0) return 0; + continue; + } + + if (start0 < src_p) + { + if (start2 < src_p + ml0) // empirical + { + src_p = start0; + xxx_ref = ref0; + ml = ml0; + } + } + + // Here, start0==ip + if ((start2 - src_p) < 3) // First Match too small : removed + { + ml = ml2; + src_p = start2; + xxx_ref = ref2; + goto _Search2; + } + + _Search3: + // Currently we have : + // ml2 > ml1, and + // ip1+3 <= ip2 (usually < ip1+ml1) + if ((start2 - src_p) < OPTIMAL_ML) + { + var new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (src_p + new_ml > start2 + ml2 - MINMATCH) new_ml = (start2 - src_p) + ml2 - MINMATCH; + var correction = new_ml - (start2 - src_p); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + // Now, we have start2 = ip+new_ml, with new_ml=min(ml, OPTIMAL_ML=18) + + var ml3 = start2 + ml2 < src_mflimit + ? LZ4HC_InsertAndGetWiderMatch_64(ctx, start2 + ml2 - 3, start2, ml2, ref ref3, ref start3) + : ml2; + + if (ml3 == ml2) // No better match : 2 sequences to encode + { + // ip & ref are known; Now for ml + if (start2 < src_p + ml) ml = (start2 - src_p); + // Now, encode 2 sequences + if (LZ4_encodeSequence_64(ctx, ref src_p, ref dst_p, ref src_anchor, ml, xxx_ref) != 0) return 0; + src_p = start2; + if (LZ4_encodeSequence_64(ctx, ref src_p, ref dst_p, ref src_anchor, ml2, ref2) != 0) return 0; + continue; + } + + if (start3 < src_p + ml + 3) // Not enough space for match 2 : remove it + { + if (start3 >= (src_p + ml)) // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 + { + if (start2 < src_p + ml) + { + var correction = (src_p + ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) + { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + if (LZ4_encodeSequence_64(ctx, ref src_p, ref dst_p, ref src_anchor, ml, xxx_ref) != 0) return 0; + src_p = start3; + xxx_ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + // OK, now we have 3 ascending matches; let's write at least the first one + // ip & ref are known; Now for ml + if (start2 < src_p + ml) + { + if ((start2 - src_p) < ML_MASK) + { + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (src_p + ml > start2 + ml2 - MINMATCH) ml = (start2 - src_p) + ml2 - MINMATCH; + var correction = ml - (start2 - src_p); + if (correction > 0) + { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + else + { + ml = (start2 - src_p); + } + } + if (LZ4_encodeSequence_64(ctx, ref src_p, ref dst_p, ref src_anchor, ml, xxx_ref) != 0) return 0; + + src_p = start2; + xxx_ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _Search3; + } + + // Encode Last Literals + { + var lastRun = (src_end - src_anchor); + if ((dst_p - dst_0) + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > (uint)dst_len) return 0; // Check output limit + if (lastRun >= RUN_MASK) + { + dst[dst_p++] = (RUN_MASK << ML_BITS); + lastRun -= RUN_MASK; + for (; lastRun > 254; lastRun -= 255) dst[dst_p++] = 255; + dst[dst_p++] = (byte)lastRun; + } + else + { + dst[dst_p++] = (byte)(lastRun << ML_BITS); + } + BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); + dst_p += src_end - src_anchor; + } + + // End + return (dst_p - dst_0); + } + } +} + +// ReSharper restore InconsistentNaming diff --git a/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs.meta new file mode 100644 index 0000000..e6f49e8 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.Safe64HC.Dirty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c61e65caf96385a469be74084e2e1f00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Codec.cs b/Assets/Scripts/LZ4/LZ4Codec.cs new file mode 100644 index 0000000..5cdf315 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.cs @@ -0,0 +1,176 @@ +#region license + +/* +Copyright (c) 2013, Milosz Krajewski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#endregion + +using System; + +/* +NOTE: + This file is shared between LZ4pn and LZ4ps. + If you would like to modify this file please keep in mind that your changes will + affect both projects. + Use 'UNSAFE' conditional define to differentiate +*/ + +// ReSharper disable InconsistentNaming + +#if UNSAFE +namespace LZ4pn +#else +namespace LZ4ps +#endif +{ + public static partial class LZ4Codec + { + #region configuration + + /// + /// Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + /// Increasing memory usage improves compression ratio + /// Reduced memory usage can improve speed, due to cache effect + /// Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + /// + private const int MEMORY_USAGE = 20; + + /// + /// Decreasing this value will make the algorithm skip faster data segments considered "incompressible" + /// This may decrease compression ratio dramatically, but will be faster on incompressible data + /// Increasing this value will make the algorithm search more before declaring a segment "incompressible" + /// This could improve compression a bit, but will be slower on incompressible data + /// The default value (6) is recommended + /// + private const int NOTCOMPRESSIBLE_DETECTIONLEVEL = 6; + +#if !UNSAFE + + /// Buffer length when Buffer.BlockCopy becomes faster than straight loop. + /// Please note that safe implementation REQUIRES it to be greater (not even equal) than 8. + private const int BLOCK_COPY_LIMIT = 16; + +#endif + + #endregion + + #region consts + + private const int MINMATCH = 4; + #pragma warning disable 162 + // ReSharper disable once UnreachableCode + private const int SKIPSTRENGTH = + NOTCOMPRESSIBLE_DETECTIONLEVEL > 2 + ? NOTCOMPRESSIBLE_DETECTIONLEVEL + : 2; + #pragma warning restore 162 + private const int COPYLENGTH = 8; + private const int LASTLITERALS = 5; + private const int MFLIMIT = COPYLENGTH + MINMATCH; + private const int MINLENGTH = MFLIMIT + 1; + private const int MAXD_LOG = 16; + private const int MAXD = 1 << MAXD_LOG; + private const int MAXD_MASK = MAXD - 1; + private const int MAX_DISTANCE = (1 << MAXD_LOG) - 1; + private const int ML_BITS = 4; + private const int ML_MASK = (1 << ML_BITS) - 1; + private const int RUN_BITS = 8 - ML_BITS; + private const int RUN_MASK = (1 << RUN_BITS) - 1; + private const int STEPSIZE_64 = 8; + private const int STEPSIZE_32 = 4; + + private const int LZ4_64KLIMIT = (1 << 16) + (MFLIMIT - 1); + + private const int HASH_LOG = MEMORY_USAGE - 2; + private const int HASH_TABLESIZE = 1 << HASH_LOG; + private const int HASH_ADJUST = (MINMATCH * 8) - HASH_LOG; + + private const int HASH64K_LOG = HASH_LOG + 1; + private const int HASH64K_TABLESIZE = 1 << HASH64K_LOG; + private const int HASH64K_ADJUST = (MINMATCH * 8) - HASH64K_LOG; + + private const int HASHHC_LOG = MAXD_LOG - 1; + private const int HASHHC_TABLESIZE = 1 << HASHHC_LOG; + private const int HASHHC_ADJUST = (MINMATCH * 8) - HASHHC_LOG; + //private const int HASHHC_MASK = HASHHC_TABLESIZE - 1; + + private static readonly int[] DECODER_TABLE_32 = { 0, 3, 2, 3, 0, 0, 0, 0 }; + private static readonly int[] DECODER_TABLE_64 = { 0, 0, 0, -1, 0, 1, 2, 3 }; + + private static readonly int[] DEBRUIJN_TABLE_32 = { + 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, + 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 + }; + + private static readonly int[] DEBRUIJN_TABLE_64 = { + 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, + 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, + 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, + 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 + }; + + private const int MAX_NB_ATTEMPTS = 256; + private const int OPTIMAL_ML = (ML_MASK - 1) + MINMATCH; + + #endregion + + #region public interface (common) + + /// Gets maximum the length of the output. + /// Length of the input. + /// Maximum number of bytes needed for compressed buffer. + public static int MaximumOutputLength(int inputLength) + { + return inputLength + (inputLength / 255) + 16; + } + + #endregion + + #region internal interface (common) + + internal static void CheckArguments( + byte[] input, int inputOffset, ref int inputLength, + byte[] output, int outputOffset, ref int outputLength) + { + if (inputLength < 0) inputLength = input.Length - inputOffset; + if (inputLength == 0) + { + outputLength = 0; + return; + } + + if (input == null) throw new ArgumentNullException("input"); + if (inputOffset < 0 || inputOffset + inputLength > input.Length) + throw new ArgumentException("inputOffset and inputLength are invalid for given input"); + + if (outputLength < 0) outputLength = output.Length - outputOffset; + if (output == null) throw new ArgumentNullException("output"); + if (outputOffset < 0 || outputOffset + outputLength > output.Length) + throw new ArgumentException("outputOffset and outputLength are invalid for given output"); + } + + #endregion + } +} + +// ReSharper restore InconsistentNaming \ No newline at end of file diff --git a/Assets/Scripts/LZ4/LZ4Codec.cs.meta b/Assets/Scripts/LZ4/LZ4Codec.cs.meta new file mode 100644 index 0000000..ea0b563 --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Codec.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60265417a9a7cb346be1757b77ecdd37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LZ4/LZ4Util.cs b/Assets/Scripts/LZ4/LZ4Util.cs new file mode 100644 index 0000000..de805dc --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Util.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +//using LZ4Sharp; +using LZ4ps; + +namespace LZ4 +{ + /// + /// 置換 + /// + public class LZ4Util + { + public static byte[] decompress(byte[] indata) + { + int m_curPos = 4; + int token = (int)(indata[m_curPos - 4] | (indata[m_curPos - 3] << 8) | (indata[m_curPos - 2] << 16) | (indata[m_curPos - 1] << 24)); + m_curPos += 4; + int decompSize = (int)(indata[m_curPos - 4] | (indata[m_curPos - 3] << 8) | (indata[m_curPos - 2] << 16) | (indata[m_curPos - 1] << 24)); + m_curPos += 4; + int compSize = (int)(indata[m_curPos - 4] | (indata[m_curPos - 3] << 8) | (indata[m_curPos - 2] << 16) | (indata[m_curPos - 1] << 24)); + m_curPos += 4; + int tmp4 = (int)(indata[m_curPos - 4] | (indata[m_curPos - 3] << 8) | (indata[m_curPos - 2] << 16) | (indata[m_curPos - 1] << 24)); + + //byte[] srcBytes = new byte[compSize]; + //Array.Copy(indata, 0x10, srcBytes, 0, compSize); + + if (IntPtr.Size == 4) + { + return LZ4Codec.Decode32(indata, 0x10, compSize, decompSize); + } + else + { + return LZ4Codec.Decode64(indata, 0x10, compSize, decompSize); + } + } + } + /* + public class LZ4Util + { + + public static byte[] decompress(byte[] indata) + { + LZ4.BinaryReader r = new LZ4.BinaryReader(indata); + + byte[] dec = decompress(r); + + return dec; + } + + public static byte[] decompress(BinaryReader r) + { + byte[] retArray; + var dataSize = 0; + var decompressedSize = 0; + + var token = 0; + var sqSize = 0; + var matchSize = 0; + var offset = 0; + var retCurPos = 0; + var endPos = 0; + + r.seekAbs(4); + decompressedSize = r.readIntLE(); + dataSize = r.readIntLE(); + endPos = dataSize + 16; + retArray = new byte[decompressedSize]; + + r.seekAbs(16); + + //Start reading sequences + while (true) + { + //read the LiteralSize and the MatchSize + token = r.readByte(); + sqSize = token >> 4; + matchSize = (token & 0x0F) + 4; + if (sqSize == 15) + sqSize += readAdditionalSize(r); + + //copy the literal + r.copyBytes(retArray, retCurPos, sqSize); + retCurPos += sqSize; + + if (r.getPos() >= endPos - 1) + break; + + //read the offset + offset = r.readShortLE(); + + //read the additional MatchSize + if (matchSize == 19) + matchSize += readAdditionalSize(r); + + //copy the match properly + if (matchSize > offset) + { + var matchPos = retCurPos - offset; + while (true) + { + Array.Copy(retArray, matchPos, retArray, retCurPos, offset); + retCurPos += offset; + matchSize -= offset; + if (matchSize < offset) + break; + } + } + Array.Copy(retArray, retCurPos - offset, retArray, retCurPos, matchSize); + retCurPos += matchSize; + } + + + return retArray; + } + + public static int readAdditionalSize(BinaryReader reader) + { + var size = reader.readByte(); + if (size == 255) + return size + readAdditionalSize(reader); + else + return size; + } + } + */ +} diff --git a/Assets/Scripts/LZ4/LZ4Util.cs.meta b/Assets/Scripts/LZ4/LZ4Util.cs.meta new file mode 100644 index 0000000..05ff99b --- /dev/null +++ b/Assets/Scripts/LZ4/LZ4Util.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a39980c7200b27b4b8357880848e8646 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Test.cs b/Assets/Scripts/Test.cs new file mode 100644 index 0000000..5bc6597 --- /dev/null +++ b/Assets/Scripts/Test.cs @@ -0,0 +1,40 @@ +using BaseNcoding; +using System; +using System.Security.Cryptography; +using System.Text; +using UnityEngine; + +namespace Assets.Editor.RootMotion +{ + public class Test : MonoBehaviour + { + private void Start() + { + SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider(); + var str = CalHameString(sha, 1541716412357448819, 77, Encoding.UTF8.GetBytes("root")); + Debug.LogWarning($"{str} Length:{str.Length}"); + Debug.LogWarning($"{"FN4II57IWE274AT5R45G2UE7XVMFDPBD"} Length:{"FN4II57IWE274AT5R45G2UE7XVMFDPBD".Length}"); + } + + public byte[] CalHame(SHA1CryptoServiceProvider sha1, ulong checksum, ulong size, byte[] name) + { + if (name == null) return null; + byte[] allbyte = new byte[name.Length + 16]; + byte[] checksumByte = BitConverter.GetBytes(checksum); + byte[] sizeByte = BitConverter.GetBytes(size); + for(int i=0; i < 16; i++) + { + allbyte[i] = (i < 8 ? checksumByte[7 - i] : sizeByte[15 - i]); + } + Buffer.BlockCopy(name, 0, allbyte, 16, name.Length); + return sha1.ComputeHash(allbyte); + } + + public string CalHameString(SHA1CryptoServiceProvider sha1, ulong checksum, ulong size, byte[] name) + { + var bytes = CalHame(sha1, checksum, size, name); + Base32 base32 = new Base32(); + return base32.Encode(bytes); + } + } +} diff --git a/Assets/Scripts/Test.cs.meta b/Assets/Scripts/Test.cs.meta new file mode 100644 index 0000000..c617097 --- /dev/null +++ b/Assets/Scripts/Test.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 822adc4dd85200142901586dd5c697c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 99b6b22..1ebb68e 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -5,7 +5,7 @@ "depth": 1, "source": "registry", "dependencies": {}, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.assetbundlebrowser": { "version": "https://github.com/Unity-Technologies/AssetBundles-Browser.git", @@ -21,14 +21,14 @@ "dependencies": { "com.unity.services.core": "1.0.1" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.ext.nunit": { "version": "1.0.6", "depth": 1, "source": "registry", "dependencies": {}, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.formats.fbx": { "version": "4.1.2", @@ -38,7 +38,7 @@ "com.unity.timeline": "1.5.2", "com.autodesk.fbx": "4.1.1" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.ide.rider": { "version": "2.0.7", @@ -47,7 +47,7 @@ "dependencies": { "com.unity.test-framework": "1.1.1" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.ide.visualstudio": { "version": "2.0.16", @@ -56,14 +56,14 @@ "dependencies": { "com.unity.test-framework": "1.1.9" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.ide.vscode": { "version": "1.2.5", "depth": 0, "source": "registry", "dependencies": {}, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.postprocessing": { "version": "3.2.2", @@ -72,7 +72,7 @@ "dependencies": { "com.unity.modules.physics": "1.0.0" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.services.core": { "version": "1.0.1", @@ -81,7 +81,7 @@ "dependencies": { "com.unity.modules.unitywebrequest": "1.0.0" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.test-framework": { "version": "1.1.31", @@ -92,7 +92,7 @@ "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.textmeshpro": { "version": "3.0.6", @@ -101,7 +101,7 @@ "dependencies": { "com.unity.ugui": "1.0.0" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.timeline": { "version": "1.5.2", @@ -113,7 +113,7 @@ "com.unity.modules.audio": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, - "url": "https://packages.unity.com" + "url": "https://packages.unity.cn" }, "com.unity.ugui": { "version": "1.0.0", diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset index be4a797..bc93024 100644 --- a/ProjectSettings/PackageManagerSettings.asset +++ b/ProjectSettings/PackageManagerSettings.asset @@ -20,7 +20,7 @@ MonoBehaviour: m_Registries: - m_Id: main m_Name: - m_Url: https://packages.unity.com + m_Url: https://packages.unity.cn m_Scopes: [] m_IsDefault: 1 m_Capabilities: 7 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 3598ae5..9b8fc7a 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2020.3.28f1 -m_EditorVersionWithRevision: 2020.3.28f1 (f5400f52e03f) +m_EditorVersion: 2020.3.24f1c2 +m_EditorVersionWithRevision: 2020.3.24f1c2 (f3616f7ef48a)