From d753a9fc3cf7bb1952a1111a92ceb69b8504ed78 Mon Sep 17 00:00:00 2001 From: "Valeriano A.R" Date: Sun, 9 Jul 2017 19:17:38 +0200 Subject: [PATCH] Base62 coding of URLs --- VAR.UrlCompressor/Base62.cs | 133 +++++++++++++++++++++ VAR.UrlCompressor/UrlCompressor.cs | 33 +++-- VAR.UrlCompressor/VAR.UrlCompressor.csproj | 1 + 3 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 VAR.UrlCompressor/Base62.cs diff --git a/VAR.UrlCompressor/Base62.cs b/VAR.UrlCompressor/Base62.cs new file mode 100644 index 0000000..0544c48 --- /dev/null +++ b/VAR.UrlCompressor/Base62.cs @@ -0,0 +1,133 @@ +using System; +using System.Text; + +namespace VAR.UrlCompressor +{ + class Base62 + { + private static string Base62CodingSpace = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + private static bool ReadBit(byte[] bytes, int position, int offset) + { + if (offset < 0) { return false; } + int tempPos = position + offset; + int bytePosition = tempPos / 8; + int bitPosition = tempPos % 8; + if (bytePosition >= bytes.Length) { return false; } + return (bytes[bytePosition] & (0x01 << (7 - bitPosition))) > 0; + } + + private static void WriteBit(byte[] bytes, int position, int offset, bool value) + { + if (offset < 0) { return; } + int tempPos = position + offset; + int bytePosition = tempPos / 8; + int bitPosition = tempPos % 8; + if (bytePosition >= bytes.Length) { return; } + if (value) + { + bytes[bytePosition] = (byte)(bytes[bytePosition] | (0x01 << (7 - bitPosition))); + } + else + { + bytes[bytePosition] = (byte)(bytes[bytePosition] & (0xffffffff - (0x1 << (7 - bitPosition)))); + } + } + + public static string Encode(byte[] original) + { + StringBuilder sb = new StringBuilder(); + + int bitPosition = 0; + int lenght = original.Length * 8; + while (bitPosition < lenght) + { + int pad = 0; + if (bitPosition + 6 > lenght) + { + pad = lenght - (bitPosition + 6); + } + bool bit0 = ReadBit(original, bitPosition, 0 + pad); + bool bit1 = ReadBit(original, bitPosition, 1 + pad); + bool bit2 = ReadBit(original, bitPosition, 2 + pad); + bool bit3 = ReadBit(original, bitPosition, 3 + pad); + bool bit4 = ReadBit(original, bitPosition, 4 + pad); + bool bit5 = ReadBit(original, bitPosition, 5 + pad); + + if (bit0 == true && bit1 == true && bit2 == true && bit3 == true && bit4 == true) + { + sb.Append(Base62CodingSpace[61]); + bitPosition += 5; + continue; + } + if (bit0 == true && bit1 == true && bit2 == true && bit3 == true && bit4 == false) + { + sb.Append(Base62CodingSpace[60]); + bitPosition += 5; + continue; + } + int charIdx = (bit0 ? 0x20 : 0) | (bit1 ? 0x10 : 0) | (bit2 ? 0x08 : 0) | (bit3 ? 0x04 : 0) | (bit4 ? 0x02 : 0) | (bit5 ? 0x01 : 0); + sb.Append(Base62CodingSpace[charIdx]); + bitPosition += 6; + } + return sb.ToString(); + } + + public static byte[] Decode(string base62) + { + byte[] bytes = new byte[base62.Length * 6 / 8]; + int bitPosition = 0; + for (int i = 0; i < base62.Length; i++) + { + int charIdx = Base62CodingSpace.IndexOf(base62[i]); + if ((i + 1) == base62.Length) + { + // Last symbol + int rest = 8 - (bitPosition % 8); + if (rest == 8) { throw new Exception("Extra symbol at end"); } + if ((charIdx >> rest) > 0) { throw new Exception("Invalid ending symbol"); } + int pad = 6 - rest; + WriteBit(bytes, bitPosition, 0 - pad, (charIdx & 0x20) > 0); + WriteBit(bytes, bitPosition, 1 - pad, (charIdx & 0x10) > 0); + WriteBit(bytes, bitPosition, 2 - pad, (charIdx & 0x08) > 0); + WriteBit(bytes, bitPosition, 3 - pad, (charIdx & 0x04) > 0); + WriteBit(bytes, bitPosition, 4 - pad, (charIdx & 0x02) > 0); + WriteBit(bytes, bitPosition, 5 - pad, (charIdx & 0x01) > 0); + + break; + } + + if (charIdx == 60) + { + WriteBit(bytes, bitPosition, 0, true); + WriteBit(bytes, bitPosition, 1, true); + WriteBit(bytes, bitPosition, 2, true); + WriteBit(bytes, bitPosition, 3, true); + WriteBit(bytes, bitPosition, 4, false); + bitPosition += 5; + } + else if (charIdx == 61) + { + WriteBit(bytes, bitPosition, 0, true); + WriteBit(bytes, bitPosition, 1, true); + WriteBit(bytes, bitPosition, 2, true); + WriteBit(bytes, bitPosition, 3, true); + WriteBit(bytes, bitPosition, 4, true); + bitPosition += 5; + } + else + { + WriteBit(bytes, bitPosition, 0, (charIdx & 0x20) > 0); + WriteBit(bytes, bitPosition, 1, (charIdx & 0x10) > 0); + WriteBit(bytes, bitPosition, 2, (charIdx & 0x08) > 0); + WriteBit(bytes, bitPosition, 3, (charIdx & 0x04) > 0); + WriteBit(bytes, bitPosition, 4, (charIdx & 0x02) > 0); + WriteBit(bytes, bitPosition, 5, (charIdx & 0x01) > 0); + bitPosition += 6; + } + } + + return bytes; + } + } +} diff --git a/VAR.UrlCompressor/UrlCompressor.cs b/VAR.UrlCompressor/UrlCompressor.cs index dedfac8..08d4305 100644 --- a/VAR.UrlCompressor/UrlCompressor.cs +++ b/VAR.UrlCompressor/UrlCompressor.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace VAR.UrlCompressor { @@ -10,14 +7,36 @@ namespace VAR.UrlCompressor { public static string Compress(string url) { - // FIXME: Implement this - return string.Empty; + // Replace protocol indicator + if (url.StartsWith("https://") || url.StartsWith("HTTPS://")) + { + url = string.Format("${0}", url.Substring("https://".Length)); + } + if (url.StartsWith("http://") || url.StartsWith("HTTP://")) + { + url = string.Format("#{0}", url.Substring("http://".Length)); + } + + byte[] urlBytes = Encoding.ASCII.GetBytes(url); + return Base62.Encode(urlBytes); } public static string Decompress(string compressedUrl) { - // FIXME: Implement this - return string.Empty; + byte[] urlBytes = Base62.Decode(compressedUrl); + string url = Encoding.ASCII.GetString(urlBytes); + + // Restore protocol indicator + if (url.StartsWith("#")) + { + url = string.Format("http://{0}", url.Substring(1)); + } + if (url.StartsWith("$")) + { + url = string.Format("https://{0}", url.Substring(1)); + } + + return url; } } } diff --git a/VAR.UrlCompressor/VAR.UrlCompressor.csproj b/VAR.UrlCompressor/VAR.UrlCompressor.csproj index 517cc1e..212bdef 100644 --- a/VAR.UrlCompressor/VAR.UrlCompressor.csproj +++ b/VAR.UrlCompressor/VAR.UrlCompressor.csproj @@ -40,6 +40,7 @@ +